From 760784cd0eb2f4050314f5735a962683969aa66c Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 20:41:38 +0500 Subject: [PATCH 1/7] ci/linux: use more recent nodejs --- ci/docker/install_node.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/docker/install_node.sh b/ci/docker/install_node.sh index f690f8dd..7ae5c56a 100755 --- a/ci/docker/install_node.sh +++ b/ci/docker/install_node.sh @@ -3,8 +3,8 @@ set -e "$(dirname $0)/install.sh" \ - 'https://nodejs.org/dist/v12.22.12/node-v12.22.12-linux-x64.tar.xz' \ - 'e6d052364bfa2c17da92cf31794100cfd709ba147415ddaeed2222eec9ca1469' + 'https://unofficial-builds.nodejs.org/download/release/v22.22.0/node-v22.22.0-linux-x64-glibc-217.tar.xz' \ + 'db4a1d582e6fffcf7fb348149ca4ac8fa685699c5bc46cd7e22bbf9a7e673454' export PATH="/usr/local/bin:$PATH" From 8895b2479f806ac35a08df25e88b28775ce5ced8 Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 21:08:54 +0500 Subject: [PATCH 2/7] api_tests: migrate to vitest --- js/api_tests/package.json | 6 +- js/api_tests/src/artwork_api_tests.js | 85 +-- js/api_tests/src/authentication_tests.js | 47 +- js/api_tests/src/browser_api_tests.js | 80 ++- js/api_tests/src/client_config_api_tests.js | 29 +- js/api_tests/src/http_features_tests.js | 34 +- js/api_tests/src/main.js | 12 - js/api_tests/src/outputs_api_tests.js | 50 +- js/api_tests/src/permissions_tests.js | 40 +- js/api_tests/src/play_queue_api_tests.js | 148 +++-- js/api_tests/src/player_api_tests.js | 556 ++++++++-------- js/api_tests/src/playlists_api_tests.js | 689 ++++++++++---------- js/api_tests/src/query_api_tests.js | 523 +++++++-------- js/api_tests/src/static_files_tests.js | 333 +++++----- js/api_tests/src/test_env.js | 26 +- js/api_tests/vitest.config.js | 13 + js/yarn.lock | 571 ++++++++++++++++ 17 files changed, 1850 insertions(+), 1392 deletions(-) delete mode 100644 js/api_tests/src/main.js create mode 100644 js/api_tests/vitest.config.js diff --git a/js/api_tests/package.json b/js/api_tests/package.json index 08e60ea4..0860c069 100644 --- a/js/api_tests/package.json +++ b/js/api_tests/package.json @@ -9,7 +9,7 @@ "license": "MIT", "private": true, "scripts": { - "test": "qunit src/main.js" + "test": "vitest" }, "dependencies": { "axios": "^0.21.2", @@ -17,7 +17,7 @@ "lodash": "^4.17.21", "mkdirp": "^1.0.4", "picomatch": "^4.0.3", - "qunit": "^2.19.3", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "vitest": "^4.0.17" } } diff --git a/js/api_tests/src/artwork_api_tests.js b/js/api_tests/src/artwork_api_tests.js index 113260df..09517d91 100644 --- a/js/api_tests/src/artwork_api_tests.js +++ b/js/api_tests/src/artwork_api_tests.js @@ -1,10 +1,9 @@ import path from 'path'; -import { promisify } from 'util'; import fs from 'fs'; -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, config, tracks, usePlayer } from './test_env.js'; -const readFile = promisify(fs.readFile); +const readFile = fs.promises.readFile; function getFile(name) { @@ -27,58 +26,60 @@ function getArtwork(playlist, item) }); } -q.module('artwork 1', usePlayer()); +describe('artwork 1', () => { + usePlayer(); -q.test('get from folder', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.waitPlaybackMetadata(); + test('get from folder', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.waitPlaybackMetadata(); - const expected = await getFile('cover.png'); - const response = await getArtwork(0, 0); + const expected = await getFile('cover.png'); + const response = await getArtwork(0, 0); - assert.equal(response.status, 200); - assert.equal(response.headers['content-type'], 'image/png'); - assert.ok(response.data.equals(expected)); -}); + assert.equal(response.status, 200); + assert.equal(response.headers['content-type'], 'image/png'); + assert.ok(response.data.equals(expected)); + }); -q.test('get current', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.play(0, 0); - await client.waitForState('playing'); - await client.waitPlaybackMetadata(); + test('get current', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); + await client.waitPlaybackMetadata(); - const expected = await getFile('cover.png'); - const response = await getCurrentArtwork(); + const expected = await getFile('cover.png'); + const response = await getCurrentArtwork(); - assert.equal(response.status, 200); - assert.equal(response.headers['content-type'], 'image/png'); - assert.ok(response.data.equals(expected)); + assert.equal(response.status, 200); + assert.equal(response.headers['content-type'], 'image/png'); + assert.ok(response.data.equals(expected)); + }); }); -q.module('artwork 2', usePlayer()); +describe('artwork 2', () => { + usePlayer(); -q.test('get from tag', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2Alt]); - await client.waitPlaybackMetadata(); + test('get from tag', async () => { + await client.addPlaylistItems(0, [tracks.t2Alt]); + await client.waitPlaybackMetadata(); - const expected = await getFile('cover-white.png.hidden'); - const response = await getArtwork(0, 0); + const expected = await getFile('cover-white.png.hidden'); + const response = await getArtwork(0, 0); - assert.equal(response.status, 200); - assert.equal(response.headers['content-type'], 'image/png'); - assert.ok(response.data.equals(expected)); + assert.equal(response.status, 200); + assert.equal(response.headers['content-type'], 'image/png'); + assert.ok(response.data.equals(expected)); + }); }); -q.module('artwork 3', usePlayer()); +describe('artwork 3', () => { + usePlayer(); -q.test('missing', async assert => -{ - await client.addPlaylistItems(0, [tracks.t3]); - await client.waitPlaybackMetadata(); + test('missing', async () => { + await client.addPlaylistItems(0, [tracks.t3]); + await client.waitPlaybackMetadata(); - const response = await getArtwork(0, 0); - assert.equal(response.status, 404); + const response = await getArtwork(0, 0); + assert.equal(response.status, 404); + }); }); diff --git a/js/api_tests/src/authentication_tests.js b/js/api_tests/src/authentication_tests.js index 9da3c6b9..b71bf7fa 100644 --- a/js/api_tests/src/authentication_tests.js +++ b/js/api_tests/src/authentication_tests.js @@ -1,4 +1,4 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import lodash from 'lodash'; import { client, usePlayer } from './test_env.js'; @@ -30,34 +30,33 @@ function makeRequest(config) return client.handler.axios.get('api/player', fullConfig); } -q.module('authentication', usePlayer({ pluginSettings, axiosConfig })); +describe('authentication', () => { + usePlayer({ pluginSettings, axiosConfig }); -q.test('require auth', async assert => -{ - const response = await makeRequest(); - - assert.equal(response.status, 401); - assert.ok(response.data && response.data.error); - assert.ok(startsWith(response.headers['www-authenticate'], 'Basic')); -}); + test('require auth', async () => { + const response = await makeRequest(); -q.test('invalid auth', async assert => -{ - const response = await makeRequest({ - auth: { username: authUser, password: 'wrong' } + assert.equal(response.status, 401); + assert.ok(response.data && response.data.error); + assert.ok(startsWith(response.headers['www-authenticate'], 'Basic')); }); - assert.equal(response.status, 401); - assert.ok(response.data && response.data.error); - assert.ok(startsWith(response.headers['www-authenticate'], 'Basic')); -}); + test('invalid auth', async () => { + const response = await makeRequest({ + auth: { username: authUser, password: 'wrong' } + }); -q.test('valid auth', async assert => -{ - const response = await makeRequest({ - auth: { username: authUser, password: authPassword } + assert.equal(response.status, 401); + assert.ok(response.data && response.data.error); + assert.ok(startsWith(response.headers['www-authenticate'], 'Basic')); }); - assert.equal(response.status, 200); - assert.ok(response.data && response.data.player); + test('valid auth', async () => { + const response = await makeRequest({ + auth: { username: authUser, password: authPassword } + }); + + assert.equal(response.status, 200); + assert.ok(response.data && response.data.player); + }); }); diff --git a/js/api_tests/src/browser_api_tests.js b/js/api_tests/src/browser_api_tests.js index aca0e627..f40ec717 100644 --- a/js/api_tests/src/browser_api_tests.js +++ b/js/api_tests/src/browser_api_tests.js @@ -1,14 +1,11 @@ import path from 'path'; -import { promisify } from 'util'; import fs from 'fs'; -import q from 'qunit'; -import lodash from 'lodash'; +import { omit, sortBy } from 'lodash'; +import { describe, test, assert } from 'vitest'; import { client, config, usePlayer } from './test_env.js'; -const { omit, sortBy } = lodash; - -const readdir = promisify(fs.readdir); -const stat = promisify(fs.stat); +const readdir = fs.promises.readdir; +const stat = fs.promises.stat; const musicSubdir = path.join(config.musicDir, 'subdir'); @@ -50,38 +47,37 @@ async function getFileSystemEntriesDirect(dirPath) return sortBy(items, ['type', 'path']); } -q.module('browser api', usePlayer()); - -q.test('get roots', async assert => -{ - const result = await client.getFileSystemRoots() - const actual = normalizeResult(result.roots); - const expected = [{ - name: config.musicDir, - path: config.musicDir, - type: 'D', - }]; - - assert.ok(isPathSeparator(result.pathSeparator)); - assert.deepEqual(actual, expected); -}); - -q.test('get entries root', async assert => -{ - const result = await client.getFileSystemEntries(config.musicDir); - const expected = await getFileSystemEntriesDirect(config.musicDir); - const actual = normalizeResult(result.entries); - - assert.ok(isPathSeparator(result.pathSeparator)); - assert.deepEqual(actual, expected); -}); - -q.test('get entries subdir', async assert => -{ - const result = await client.getFileSystemEntries(musicSubdir); - const expected = await getFileSystemEntriesDirect(musicSubdir); - const actual = normalizeResult(result.entries); - - assert.ok(isPathSeparator(result.pathSeparator)); - assert.deepEqual(actual, expected); -}); +describe('browser api', () => { + usePlayer(); + + test('get roots', async () => { + const result = await client.getFileSystemRoots() + const actual = normalizeResult(result.roots); + const expected = [{ + name: config.musicDir, + path: config.musicDir, + type: 'D', + }]; + + assert.ok(isPathSeparator(result.pathSeparator)); + assert.deepEqual(actual, expected); + }); + + test('get entries root', async () => { + const result = await client.getFileSystemEntries(config.musicDir); + const expected = await getFileSystemEntriesDirect(config.musicDir); + const actual = normalizeResult(result.entries); + + assert.ok(isPathSeparator(result.pathSeparator)); + assert.deepEqual(actual, expected); + }); + + test('get entries subdir', async () => { + const result = await client.getFileSystemEntries(musicSubdir); + const expected = await getFileSystemEntriesDirect(musicSubdir); + const actual = normalizeResult(result.entries); + + assert.ok(isPathSeparator(result.pathSeparator)); + assert.deepEqual(actual, expected); + }); +}); \ No newline at end of file diff --git a/js/api_tests/src/client_config_api_tests.js b/js/api_tests/src/client_config_api_tests.js index a04fe1d5..73494f35 100644 --- a/js/api_tests/src/client_config_api_tests.js +++ b/js/api_tests/src/client_config_api_tests.js @@ -1,22 +1,23 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, usePlayer } from './test_env.js'; -q.module('client config api', usePlayer()); +describe('client config api', () => { + usePlayer(); -q.test('client config', async assert => -{ - const id = 'api_test_' + Math.round(1000000 * Math.random()); + test('client config', async () => { + const id = 'api_test_' + Math.round(1000000 * Math.random()); - const config1 = await client.getClientConfig(id); - assert.equal(config1, null); + const config1 = await client.getClientConfig(id); + assert.equal(config1, null); - const value = { prop1: true, prop2: 'hello', prop3: { prop4: 123, prop5: [ 'item' ] } }; - await client.setClientConfig(id, value); + const value = { prop1: true, prop2: 'hello', prop3: { prop4: 123, prop5: ['item'] } }; + await client.setClientConfig(id, value); - const config2 = await client.getClientConfig(id); - assert.deepEqual(config2, value); + const config2 = await client.getClientConfig(id); + assert.deepEqual(config2, value); - await client.removeClientConfig(id); - const config3 = await client.getClientConfig(id); - assert.deepEqual(config3, null); + await client.removeClientConfig(id); + const config3 = await client.getClientConfig(id); + assert.deepEqual(config3, null); + }); }); diff --git a/js/api_tests/src/http_features_tests.js b/js/api_tests/src/http_features_tests.js index ade8ea5b..0b8b2fbb 100644 --- a/js/api_tests/src/http_features_tests.js +++ b/js/api_tests/src/http_features_tests.js @@ -1,4 +1,4 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, tracks, usePlayer } from './test_env.js'; const expectedValue = 'Very Custom, Much Configurable, Wow'; @@ -9,25 +9,25 @@ const pluginSettings = { } }; -q.module('http features', usePlayer({ pluginSettings })); +describe('http features', () => { + usePlayer({ pluginSettings }); -q.test('custom headers', async assert => -{ - const response = await client.handler.axios.get('/api/player'); + test('custom headers', async () => { + const response = await client.handler.axios.get('/api/player'); - assert.strictEqual(response.headers['x-customheader'], expectedValue); -}); + assert.strictEqual(response.headers['x-customheader'], expectedValue); + }); -q.test('custom headers for async method', async assert => -{ - const response = await client.handler.axios.post('/api/playlists/0/items/add', { items: [tracks.t1] }); + test('custom headers for async method', async () => { + const response = await client.handler.axios.post('/api/playlists/0/items/add', { items: [tracks.t1] }); - assert.strictEqual(response.headers['x-customheader'], expectedValue); -}); + assert.strictEqual(response.headers['x-customheader'], expectedValue); + }); -q.test('options method', async assert => -{ - const response = await client.handler.axios.options('/api/player'); + test('options method', async () => { + const response = await client.handler.axios.options('/api/player'); - assert.strictEqual(response.headers['x-customheader'], expectedValue); -}); + assert.strictEqual(response.headers['x-customheader'], expectedValue); + }); + +}); \ No newline at end of file diff --git a/js/api_tests/src/main.js b/js/api_tests/src/main.js deleted file mode 100644 index fa678ac3..00000000 --- a/js/api_tests/src/main.js +++ /dev/null @@ -1,12 +0,0 @@ -import './artwork_api_tests.js'; -import './authentication_tests.js'; -import './browser_api_tests.js'; -import './client_config_api_tests.js' -import './http_features_tests.js'; -import './outputs_api_tests.js'; -import './permissions_tests.js'; -import './player_api_tests.js'; -import './playlists_api_tests.js'; -import './play_queue_api_tests.js' -import './query_api_tests.js'; -import './static_files_tests.js'; diff --git a/js/api_tests/src/outputs_api_tests.js b/js/api_tests/src/outputs_api_tests.js index a0daeef2..94f2453b 100644 --- a/js/api_tests/src/outputs_api_tests.js +++ b/js/api_tests/src/outputs_api_tests.js @@ -1,33 +1,33 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, outputConfigs, usePlayer } from './test_env.js'; -q.module('outputs api', usePlayer()); +describe('outputs api', () => { + usePlayer(); -q.test('get outputs config', async assert => -{ - const outputs = await client.getOutputs(); + test('get outputs config', async () => { + const outputs = await client.getOutputs(); - assert.ok(typeof outputs.supportsMultipleOutputTypes === 'boolean'); - assert.deepEqual(outputs.active, outputConfigs.default); - assert.ok(Array.isArray(outputs.types)); + assert.ok(typeof outputs.supportsMultipleOutputTypes === 'boolean'); + assert.deepEqual(outputs.active, outputConfigs.default); + assert.ok(Array.isArray(outputs.types)); - const type = outputs.types[0]; - assert.ok(type.id); - assert.ok(type.name); - assert.ok(Array.isArray(type.devices)); + const type = outputs.types[0]; + assert.ok(type.id); + assert.ok(type.name); + assert.ok(Array.isArray(type.devices)); - const device = type.devices[0]; - assert.ok(device.id); - assert.ok(device.name); -}); + const device = type.devices[0]; + assert.ok(device.id); + assert.ok(device.name); + }); -q.test('set output device', async assert => -{ - for (const config of outputConfigs.alternate) - { - await client.setOutputDevice(config.typeId, config.deviceId); - const output = (await client.getOutputs()).active; - assert.equal(output.typeId, config.typeId); - assert.equal(output.deviceId, config.deviceId); - } + test('set output device', async () => { + for (const config of outputConfigs.alternate) + { + await client.setOutputDevice(config.typeId, config.deviceId); + const output = (await client.getOutputs()).active; + assert.equal(output.typeId, config.typeId); + assert.equal(output.deviceId, config.deviceId); + } + }); }); diff --git a/js/api_tests/src/permissions_tests.js b/js/api_tests/src/permissions_tests.js index 12978d24..07d0af2f 100644 --- a/js/api_tests/src/permissions_tests.js +++ b/js/api_tests/src/permissions_tests.js @@ -1,4 +1,4 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, outputConfigs, usePlayer } from './test_env.js'; const pluginSettings = { @@ -23,28 +23,26 @@ function post(path, data) return client.handler.axios.post(path, data, axiosConfig); } -q.module('permissions', usePlayer({ pluginSettings, resetOptions })); +describe('permissions', () => { + usePlayer({ pluginSettings, resetOptions }); -q.test('get permissions', async assert => -{ - const state = await client.getPlayerState(); - assert.deepEqual(state.permissions, pluginSettings.permissions); -}); + test('get permissions', async () => { + const state = await client.getPlayerState(); + assert.deepEqual(state.permissions, pluginSettings.permissions); + }); -q.test('change playlist', async assert => -{ - const response = await post('/api/playlists/add'); - assert.equal(response.status, 403); -}); + test('change playlist', async () => { + const response = await post('/api/playlists/add'); + assert.equal(response.status, 403); + }); -q.test('change output', async assert => -{ - const response = await post('/api/outputs/active', outputConfigs.alternate[0]); - assert.equal(response.status, 403); -}); + test('change output', async () => { + const response = await post('/api/outputs/active', outputConfigs.alternate[0]); + assert.equal(response.status, 403); + }); -q.test('change client config', async assert => -{ - const response = await post('/api/clientconfig/perm_test', {}); - assert.equal(response.status, 403); + test('change client config', async () => { + const response = await post('/api/clientconfig/perm_test', {}); + assert.equal(response.status, 403); + }); }); diff --git a/js/api_tests/src/play_queue_api_tests.js b/js/api_tests/src/play_queue_api_tests.js index 4596c5f9..ec851f95 100644 --- a/js/api_tests/src/play_queue_api_tests.js +++ b/js/api_tests/src/play_queue_api_tests.js @@ -1,4 +1,4 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, config, tracks, usePlayer } from './test_env.js'; // DeaDBeeF clears play queue asynchronously on stop @@ -7,105 +7,103 @@ const resetOptions = { playerState: false }; -q.module('play queue api', usePlayer({ resetOptions })); +describe('play queue api', () => { + usePlayer({ resetOptions }); -async function setupTracks() -{ - await client.addPlaylist(); + async function setupTracks() + { + await client.addPlaylist(); - const playlists = await client.getPlaylists(); + const playlists = await client.getPlaylists(); - const p1 = playlists[0].id; - const p2 = playlists[1].id; + const p1 = playlists[0].id; + const p2 = playlists[1].id; - await client.addPlaylistItems(p1, [tracks.t1]); - await client.addPlaylistItems(p1, [tracks.t2]); - await client.addPlaylistItems(p2, [tracks.t3]); + await client.addPlaylistItems(p1, [tracks.t1]); + await client.addPlaylistItems(p1, [tracks.t2]); + await client.addPlaylistItems(p2, [tracks.t3]); - return [p1, p2]; -} + return [p1, p2]; + } -q.test('get play queue with columns', async assert => { - const [p1] = await setupTracks(); + test('get play queue with columns', async () => { + const [p1] = await setupTracks(); - await client.addToPlayQueue(p1, 0); - await client.addToPlayQueue(p1, 1); + await client.addToPlayQueue(p1, 0); + await client.addToPlayQueue(p1, 1); - const queue = await client.getPlayQueue(['%artist%', '%title%']); + const queue = await client.getPlayQueue(['%artist%', '%title%']); - assert.deepEqual(queue, [ - { playlistIndex: 0, playlistId: p1, itemIndex: 0, columns: ['Hyperblast', 'Silence Rocks - Part 1'] }, - { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: ['Hyperblast', 'Silence Rocks - Part 2'] }, - ]); -}) + assert.deepEqual(queue, [ + { playlistIndex: 0, playlistId: p1, itemIndex: 0, columns: ['Hyperblast', 'Silence Rocks - Part 1'] }, + { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: ['Hyperblast', 'Silence Rocks - Part 2'] }, + ]); + }) -q.test('add to queue', async assert => -{ - const [p1, p2] = await setupTracks(); + test('add to queue', async () => { + const [p1, p2] = await setupTracks(); - await client.addToPlayQueue(p1, 1); - await client.addToPlayQueue(1, 0); + await client.addToPlayQueue(p1, 1); + await client.addToPlayQueue(1, 0); - const queue = await client.getPlayQueue(); + const queue = await client.getPlayQueue(); - assert.deepEqual(queue, [ - { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, - { playlistIndex: 1, playlistId: p2, itemIndex: 0, columns: [] }, - ]); -}); + assert.deepEqual(queue, [ + { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, + { playlistIndex: 1, playlistId: p2, itemIndex: 0, columns: [] }, + ]); + }); -q.test('add to queue at index', async assert => -{ - if (config.playerId !== 'deadbeef') - { - assert.ok('adding to queue at index is not supported by current player'); - return; - } + test('add to queue at index', async () => { + if (config.playerId !== 'deadbeef') + { + assert.ok('adding to queue at index is not supported by current player'); + return; + } - const [p1, p2] = await setupTracks(); + const [p1, p2] = await setupTracks(); - await client.addToPlayQueue(p1, 0); - await client.addToPlayQueue(0, 1, 1); - await client.addToPlayQueue(p2, 0, 0); + await client.addToPlayQueue(p1, 0); + await client.addToPlayQueue(0, 1, 1); + await client.addToPlayQueue(p2, 0, 0); - let queue = await client.getPlayQueue(); + let queue = await client.getPlayQueue(); - assert.deepEqual(queue, [ - { playlistIndex: 1, playlistId: p2, itemIndex: 0, columns: [] }, - { playlistIndex: 0, playlistId: p1, itemIndex: 0, columns: [] }, - { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, - ]); -}); + assert.deepEqual(queue, [ + { playlistIndex: 1, playlistId: p2, itemIndex: 0, columns: [] }, + { playlistIndex: 0, playlistId: p1, itemIndex: 0, columns: [] }, + { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, + ]); + }); -q.test('remove from queue', async assert => -{ - const [p1, p2] = await setupTracks(); + test('remove from queue', async () => { + const [p1, p2] = await setupTracks(); - await client.addToPlayQueue(p1, 0); - await client.addToPlayQueue(p1, 1); - await client.addToPlayQueue(p2, 0); + await client.addToPlayQueue(p1, 0); + await client.addToPlayQueue(p1, 1); + await client.addToPlayQueue(p2, 0); - await client.removeFromPlayQueueByQueueIndex(0); - await client.removeFromPlayQueueByItemIndex(p2, 0); + await client.removeFromPlayQueueByQueueIndex(0); + await client.removeFromPlayQueueByItemIndex(p2, 0); - let queue = await client.getPlayQueue(); + let queue = await client.getPlayQueue(); - assert.deepEqual(queue, [ - { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, - ]); -}); + assert.deepEqual(queue, [ + { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: [] }, + ]); + }); -q.test('clear queue', async assert => -{ - const [p1, p2] = await setupTracks(); + test('clear queue', async () => { + const [p1, p2] = await setupTracks(); - await client.addToPlayQueue(p1, 0); - await client.addToPlayQueue(p1, 1); - await client.addToPlayQueue(p2, 0); + await client.addToPlayQueue(p1, 0); + await client.addToPlayQueue(p1, 1); + await client.addToPlayQueue(p2, 0); - await client.clearPlayQueue(); + await client.clearPlayQueue(); - let queue = await client.getPlayQueue(); + let queue = await client.getPlayQueue(); - assert.equal(queue.length, 0); -}); + assert.equal(queue.length, 0); + }); +}); \ No newline at end of file diff --git a/js/api_tests/src/player_api_tests.js b/js/api_tests/src/player_api_tests.js index 1f3327fe..ac36a8d9 100644 --- a/js/api_tests/src/player_api_tests.js +++ b/js/api_tests/src/player_api_tests.js @@ -1,372 +1,352 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest' import { client, config, tracks, usePlayer } from './test_env.js'; -q.module('player api', usePlayer()); - -q.test('get state', async assert => -{ - const state = await client.getPlayerState(); - assert.ok(state); - - const { info, activeItem, playbackState, volume, playbackMode, playbackModes } = state; - - assert.ok(info); - assert.ok(info.name); - assert.ok(info.title); - assert.ok(info.version); - assert.ok(info.pluginVersion); - - assert.ok(activeItem); - const { playlistId, playlistIndex, index, position, duration } = activeItem; - assert.equal(typeof playlistId, 'string'); - assert.equal(typeof playlistIndex, 'number'); - assert.equal(typeof index, 'number'); - assert.equal(typeof position, 'number'); - assert.equal(typeof duration, 'number'); - - assert.ok(playbackState); - assert.equal(typeof playbackState, 'string'); - - assert.ok(volume); - const { type, min, max, value, isMuted } = volume; - assert.equal(typeof type, 'string'); - assert.equal(typeof min, 'number'); - assert.equal(typeof max, 'number'); - assert.equal(typeof value, 'number'); - assert.equal(typeof isMuted, 'boolean'); - - assert.equal(typeof playbackMode, 'number'); - assert.ok(Array.isArray(playbackModes)); -}); +describe('player api', () => { + usePlayer(); + + test('get state', async () => { + const state = await client.getPlayerState(); + assert.ok(state); + + const { info, activeItem, playbackState, volume, playbackMode, playbackModes } = state; + + assert.ok(info); + assert.ok(info.name); + assert.ok(info.title); + assert.ok(info.version); + assert.ok(info.pluginVersion); + + assert.ok(activeItem); + const { playlistId, playlistIndex, index, position, duration } = activeItem; + assert.equal(typeof playlistId, 'string'); + assert.equal(typeof playlistIndex, 'number'); + assert.equal(typeof index, 'number'); + assert.equal(typeof position, 'number'); + assert.equal(typeof duration, 'number'); + + assert.ok(playbackState); + assert.equal(typeof playbackState, 'string'); + + assert.ok(volume); + const { type, min, max, value, isMuted } = volume; + assert.equal(typeof type, 'string'); + assert.equal(typeof min, 'number'); + assert.equal(typeof max, 'number'); + assert.equal(typeof value, 'number'); + assert.equal(typeof isMuted, 'boolean'); + + assert.equal(typeof playbackMode, 'number'); + assert.ok(Array.isArray(playbackModes)); + }); -q.test('query current track', async assert => -{ - await client.addPlaylistItems(0, [tracks.t3]); - - await client.play(0, 0); - await client.waitForState('playing'); - await client.waitPlaybackMetadata(); - - let backslash = '\\'; - - if (config.playerId === 'deadbeef') - backslash = '\\\\'; - - const state = await client.getPlayerState([ - '%genre%', - '%artist%', - '%album%', - '%tracknumber%', - '%title%', - '%length%', - ` %artist% ${backslash} %album%, %title% `, - ]); - - assert.deepEqual(state.activeItem.columns, [ - 'Ambient', - 'Hyperblast', - 'Silence Rocks', - '03', - 'Silence Rocks - Part 3', - '1:10', - ' Hyperblast \\ Silence Rocks, Silence Rocks - Part 3 ' - ]); -}); + test('query current track', async () => { + await client.addPlaylistItems(0, [tracks.t3]); + + await client.play(0, 0); + await client.waitForState('playing'); + await client.waitPlaybackMetadata(); + + let backslash = '\\'; + + if (config.playerId === 'deadbeef') + backslash = '\\\\'; + + const state = await client.getPlayerState([ + '%genre%', + '%artist%', + '%album%', + '%tracknumber%', + '%title%', + '%length%', + ` %artist% ${backslash} %album%, %title% `, + ]); + + assert.deepEqual(state.activeItem.columns, [ + 'Ambient', + 'Hyperblast', + 'Silence Rocks', + '03', + 'Silence Rocks - Part 3', + '1:10', + ' Hyperblast \\ Silence Rocks, Silence Rocks - Part 3 ' + ]); + }); -q.test('set volume absolute', async assert => -{ - let state = await client.getPlayerState(); - const newVolume = (state.volume.min + state.volume.max) / 2; - await client.setVolume(newVolume); + test('set volume absolute', async () => { + let state = await client.getPlayerState(); + const newVolume = (state.volume.min + state.volume.max) / 2; + await client.setVolume(newVolume); - state = await client.getPlayerState(); - assert.equal(state.volume.value, newVolume); -}); + state = await client.getPlayerState(); + assert.equal(state.volume.value, newVolume); + }); -q.test('set volume relative', async assert => -{ - const state1 = await client.getPlayerState(); + test('set volume relative', async () => { + const state1 = await client.getPlayerState(); - await client.setVolumeRelative(-1); - const state2 = await client.getPlayerState(); - assert.ok(state2.volume.value < state1.volume.value); + await client.setVolumeRelative(-1); + const state2 = await client.getPlayerState(); + assert.ok(state2.volume.value < state1.volume.value); - await client.setVolumeRelative(1); - const state3 = await client.getPlayerState(); - assert.equal(state3.volume.value, state1.volume.value); -}); + await client.setVolumeRelative(1); + const state3 = await client.getPlayerState(); + assert.equal(state3.volume.value, state1.volume.value); + }); -q.test('volume up/down', async assert => -{ - const state1 = await client.getPlayerState(); + test('volume up/down', async () => { + const state1 = await client.getPlayerState(); - await client.volumeDown(); - const state2 = await client.getPlayerState(); - assert.ok(state2.volume.value < state1.volume.value); + await client.volumeDown(); + const state2 = await client.getPlayerState(); + assert.ok(state2.volume.value < state1.volume.value); - await client.volumeUp(); - const state3 = await client.getPlayerState(); - assert.equal(state3.volume.value, state1.volume.value); -}); + await client.volumeUp(); + const state3 = await client.getPlayerState(); + assert.equal(state3.volume.value, state1.volume.value); + }); -q.test('set muted', async assert => -{ - await client.setMuted(true); + test('set muted', async () => { + await client.setMuted(true); - const state = await client.getPlayerState(); - assert.equal(state.volume.isMuted, true); -}); + const state = await client.getPlayerState(); + assert.equal(state.volume.isMuted, true); + }); -q.test('set playback mode', async assert => -{ - let state = await client.getPlayerState(); - const newMode = state.playbackModes.length - 1; - await client.setPlaybackMode(newMode); + test('set playback mode', async () => { + let state = await client.getPlayerState(); + const newMode = state.playbackModes.length - 1; + await client.setPlaybackMode(newMode); - state = await client.getPlayerState(); - assert.equal(state.playbackMode, newMode); -}); + state = await client.getPlayerState(); + assert.equal(state.playbackMode, newMode); + }); -q.test('set bool option', async assert => -{ - let state = await client.getPlayerState(); - let option = state.options.find(o => o.type === 'bool'); - assert.ok(option.name); - assert.equal(option.value, false); + test('set bool option', async () => { + let state = await client.getPlayerState(); + let option = state.options.find(o => o.type === 'bool'); + assert.ok(option.name); + assert.equal(option.value, false); - await client.setOption(option.id, true); + await client.setOption(option.id, true); - state = await client.getPlayerState(); - option = state.options.find(o => o.id === option.id); - assert.ok(option.value); -}); + state = await client.getPlayerState(); + option = state.options.find(o => o.id === option.id); + assert.ok(option.value); + }); -q.test('set enum option', async assert => -{ - let state = await client.getPlayerState(); - let option = state.options.find(o => o.type === 'enum'); - assert.ok(option.name); - assert.ok(Array.isArray(option.enumNames)); - assert.equal(option.value, 0); + test('set enum option', async () => { + let state = await client.getPlayerState(); + let option = state.options.find(o => o.type === 'enum'); + assert.ok(option.name); + assert.ok(Array.isArray(option.enumNames)); + assert.equal(option.value, 0); - await client.setOption(option.id, 1); + await client.setOption(option.id, 1); - state = await client.getPlayerState(); - option = state.options.find(o => o.id === option.id); - assert.equal(option.value, 1); -}); + state = await client.getPlayerState(); + option = state.options.find(o => o.id === option.id); + assert.equal(option.value, 1); + }); -q.test('play', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + test('play', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); - await client.play(0, 1); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 1; + await client.play(0, 1); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 1; + }); + + assert.ok(true); }); - assert.ok(true); -}); + test('stop', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('stop', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); - await client.play(0, 0); - await client.waitForState('playing'); + await client.stop(); + await client.waitForState('stopped'); - await client.stop(); - await client.waitForState('stopped'); + assert.ok(true); + }); - assert.ok(true); -}); + test('play random', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); -q.test('play random', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); + await client.playRandom(); + await client.waitForState('playing'); - await client.playRandom(); - await client.waitForState('playing'); + assert.ok(true); + }); - assert.ok(true); -}); + test('play current', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('play current', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); - await client.play(0, 0); - await client.waitForState('playing'); + await client.pause(); + await client.waitForState('paused'); - await client.pause(); - await client.waitForState('paused'); + await client.playCurrent(); + await client.waitForState('playing'); - await client.playCurrent(); - await client.waitForState('playing'); + assert.ok(true); + }); - assert.ok(true); -}); + test('pause', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('pause', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); - await client.play(0, 0); - await client.waitForState('playing'); + await client.pause(); + await client.waitForState('paused'); - await client.pause(); - await client.waitForState('paused'); + assert.ok(true); + }); - assert.ok(true); -}); + test('toggle pause', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('toggle pause', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); - await client.play(0, 0); - await client.waitForState('playing'); + await client.togglePause(); + await client.waitForState('paused'); - await client.togglePause(); - await client.waitForState('paused'); + await client.togglePause(); + await client.waitForState('playing'); - await client.togglePause(); - await client.waitForState('playing'); + assert.ok(true); + }); - assert.ok(true); -}); + test('play or pause', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('play or pause', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + await client.playOrPause(); + await client.waitForState('playing'); - await client.playOrPause(); - await client.waitForState('playing'); + await client.playOrPause(); + await client.waitForState('paused'); - await client.playOrPause(); - await client.waitForState('paused'); + await client.playOrPause(); + await client.waitForState('playing'); - await client.playOrPause(); - await client.waitForState('playing'); + assert.ok(true); + }); - assert.ok(true); -}); + test('next', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); -q.test('next', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + await client.play(0, 0); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.play(0, 0); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; - }); + await client.next(); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 1; + }); - await client.next(); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 1; + assert.ok(true); }); - assert.ok(true); -}); + test('next by', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); -q.test('next by', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); + await client.play(0, 0); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.play(0, 0); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; - }); + await client.next({ by: '%title%' }); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 2; + }); - await client.next({ by: '%title%' }); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 2; + assert.ok(true); }); - assert.ok(true); -}); + test('previous', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); -q.test('previous', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + await client.play(0, 1); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 1; + }); - await client.play(0, 1); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 1; - }); + await client.previous(); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.previous(); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; + assert.ok(true); }); - assert.ok(true); -}); + test('previous by', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t2]); -q.test('previous by', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t2]); + await client.play(0, 2); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 2; + }); - await client.play(0, 2); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 2; - }); + await client.previous({ by: '%title%' }); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.previous({ by: '%title%' }); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; + assert.ok(true); }); - assert.ok(true); -}); + test('set playback position', async () => { + await client.addPlaylistItems(0, [tracks.t3]); -q.test('set playback position', async assert => -{ - await client.addPlaylistItems(0, [tracks.t3]); + await client.play(0, 0); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.play(0, 0); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; - }); + await client.pause(); + await client.waitForState('paused'); - await client.pause(); - await client.waitForState('paused'); + await client.setPlaybackPosition(33.0); + await client.waitForState(s => { + return s.activeItem.position === 33.0; + }); - await client.setPlaybackPosition(33.0); - await client.waitForState(s => { - return s.activeItem.position === 33.0; + assert.ok(true); }); - assert.ok(true); -}); + test('move playback position', async () => { + await client.addPlaylistItems(0, [tracks.t3]); -q.test('move playback position', async assert => -{ - await client.addPlaylistItems(0, [tracks.t3]); + await client.play(0, 0); + await client.waitForState(s => { + return s.playbackState === 'playing' && s.activeItem.index === 0; + }); - await client.play(0, 0); - await client.waitForState(s => { - return s.playbackState === 'playing' && s.activeItem.index === 0; - }); + await client.pause(); + await client.waitForState('paused'); - await client.pause(); - await client.waitForState('paused'); + await client.setPlaybackPosition(33.0); + await client.waitForState(s => { + return s.activeItem.position === 33.0; + }); - await client.setPlaybackPosition(33.0); - await client.waitForState(s => { - return s.activeItem.position === 33.0; - }); + await client.movePlaybackPosition(11.0); + await client.waitForState(s => { + return s.activeItem.position === 44.0; + }); - await client.movePlaybackPosition(11.0); - await client.waitForState(s => { - return s.activeItem.position === 44.0; + assert.ok(true); }); - - assert.ok(true); }); diff --git a/js/api_tests/src/playlists_api_tests.js b/js/api_tests/src/playlists_api_tests.js index 641d2e9b..f3aa37ec 100644 --- a/js/api_tests/src/playlists_api_tests.js +++ b/js/api_tests/src/playlists_api_tests.js @@ -1,413 +1,384 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { pathCollectionsEqual, waitUntil } from './utils.js'; -import { client, config, tracks, usePlayer } from './test_env.js'; +import { client, tracks, usePlayer } from './test_env.js'; -q.module('playlists api', usePlayer()); +describe('playlists api', () => { + usePlayer(); -function assertPlaylist(assert, playlist) -{ - assert.equal(typeof playlist.id, 'string'); - assert.equal(typeof playlist.title, 'string'); + function assertPlaylist(assert, playlist) + { + assert.equal(typeof playlist.id, 'string'); + assert.equal(typeof playlist.title, 'string'); + + delete playlist.id; + delete playlist.title; + + assert.deepEqual(playlist, { + index: 0, + isCurrent: true, + itemCount: 0, + totalTime: 0, + }); + } + + test('get playlists', async () => { + const playlists = await client.getPlaylists(); + + assert.ok(playlists); + assert.equal(playlists.length, 1); + + const playlist = playlists[0]; - delete playlist.id; - delete playlist.title; + assertPlaylist(assert, playlist); + }); + + test('get single playlist', async () => { + const p1 = await client.getPlaylist('current'); + const p2 = await client.getPlaylist(0); - assert.deepEqual(playlist, { - index: 0, - isCurrent: true, - itemCount: 0, - totalTime: 0, + assert.deepEqual(p1, p2); + assertPlaylist(assert, p1); }); -} - -q.test('get playlists', async assert => -{ - const playlists = await client.getPlaylists(); - - assert.ok(playlists); - assert.equal(playlists.length, 1); - - const playlist = playlists[0]; - - assertPlaylist(assert, playlist); -}); - -q.test('get single playlist', async assert => -{ - const p1 = await client.getPlaylist('current'); - const p2 = await client.getPlaylist(0); - - assert.deepEqual(p1, p2); - assertPlaylist(assert, p1); -}); - -q.test('get playlist items', async assert => -{ - const columns = ['%track%']; - - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); - - const expected = [ - { columns: ['01'] }, - { columns: ['02'] }, - { columns: ['03'] }, - ]; - - const result1 = await client.getPlaylistItems(0, columns, '0:3'); - assert.equal(result1.offset, 0); - assert.equal(result1.totalCount, 3); - assert.deepEqual(result1.items, expected); - - const result2 = await client.getPlaylistItems(0, columns, '0:2'); - assert.equal(result2.offset, 0); - assert.equal(result2.totalCount, 3); - assert.deepEqual(result2.items, expected.slice(0, 2)); - - const result3 = await client.getPlaylistItems(0, columns, '2:2'); - assert.equal(result3.offset, 2); - assert.equal(result3.totalCount, 3); - assert.deepEqual(result3.items, expected.slice(2, 3)); - - const result4 = await client.getPlaylistItems(0, columns, '4:4'); - assert.equal(result4.offset, 3); - assert.equal(result4.totalCount, 3); - assert.deepEqual(result4.items, []); -}); - -q.test('add playlist simple', async assert => -{ - await client.addPlaylist(); - - const playlists = await client.getPlaylists(); - assert.equal(playlists.length, 2); - - const playlist = playlists[1]; - assert.equal(typeof playlist.id, 'string'); - delete playlist.id; - - assert.deepEqual(playlist, { - index: 1, - title: 'New playlist', - isCurrent: false, - itemCount: 0, - totalTime: 0, + + test('get playlist items', async () => { + const columns = ['%track%']; + + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); + + const expected = [ + { columns: ['01'] }, + { columns: ['02'] }, + { columns: ['03'] }, + ]; + + const result1 = await client.getPlaylistItems(0, columns, '0:3'); + assert.equal(result1.offset, 0); + assert.equal(result1.totalCount, 3); + assert.deepEqual(result1.items, expected); + + const result2 = await client.getPlaylistItems(0, columns, '0:2'); + assert.equal(result2.offset, 0); + assert.equal(result2.totalCount, 3); + assert.deepEqual(result2.items, expected.slice(0, 2)); + + const result3 = await client.getPlaylistItems(0, columns, '2:2'); + assert.equal(result3.offset, 2); + assert.equal(result3.totalCount, 3); + assert.deepEqual(result3.items, expected.slice(2, 3)); + + const result4 = await client.getPlaylistItems(0, columns, '4:4'); + assert.equal(result4.offset, 3); + assert.equal(result4.totalCount, 3); + assert.deepEqual(result4.items, []); }); -}); -q.test('add playlist full', async assert => -{ - await client.addPlaylist({ title: 'My playlist', index: 0, setCurrent: true }); + test('add playlist simple', async () => { + await client.addPlaylist(); - const playlists = await client.getPlaylists(); - assert.equal(playlists.length, 2); + const playlists = await client.getPlaylists(); + assert.equal(playlists.length, 2); - const playlist = playlists[0]; - assert.equal(typeof playlist.id, 'string'); - delete playlist.id; + const playlist = playlists[1]; + assert.equal(typeof playlist.id, 'string'); + delete playlist.id; - assert.deepEqual(playlist, { - index: 0, - title: 'My playlist', - isCurrent: true, - itemCount: 0, - totalTime: 0, + assert.deepEqual(playlist, { + index: 1, + title: 'New playlist', + isCurrent: false, + itemCount: 0, + totalTime: 0, + }); }); -}); - -q.test('set current playlist', async assert => -{ - await client.addPlaylist(); - await client.setCurrentPlaylist(1); - - const playlists = await client.getPlaylists(); - assert.equal(playlists[0].isCurrent, false); - assert.equal(playlists[1].isCurrent, true); -}); - -q.test('remove playlist', async assert => -{ - await client.addPlaylist(); - let playlists = await client.getPlaylists(); - - const id = playlists[1].id; - await client.removePlaylist(id); - - playlists = await client.getPlaylists(); - assert.equal(playlists.findIndex(i => i.id === id), -1); -}); - -q.test('move playlist', async assert => -{ - await client.addPlaylist(); - await client.addPlaylist(); - let playlists = await client.getPlaylists(); - - const id1 = playlists[1].id; - const id2 = playlists[2].id; - - await client.movePlaylist(id1, 2); - - playlists = await client.getPlaylists(); - assert.equal(playlists[1].id, id2); - assert.equal(playlists[2].id, id1); -}); - -q.test('clear playlist', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.clearPlaylist(0); - - const files = await client.getPlaylistFiles(0); - assert.deepEqual(files, []); -}); - -q.test('rename playlist', async assert => -{ - await client.renamePlaylist(0, 'My cool list'); - const playlists = await client.getPlaylists(); - assert.equal(playlists[0].title, 'My cool list'); -}); - -q.test('add playlist items single', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1])); -}); - -q.test('add playlist items url', async assert => -{ - const streamUrl = 'https://upload.wikimedia.org/wikipedia/en/d/d0/Rick_Astley_-_Never_Gonna_Give_You_Up.ogg'; - - await client.addPlaylistItems(0, [streamUrl]); - - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [streamUrl])); -}); - -q.test('add playlist items multiple', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); - - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3], true)); -}); - -q.test('add playlist items to position', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t1], { index: 0 }); - await client.addPlaylistItems(0, [tracks.t3], { index: 1 }); - - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t3, tracks.t2])); -}); - -q.test('add playlist items async', async assert => -{ - await client.addPlaylistItems( - 0, [tracks.t1, tracks.t2, tracks.t3], { async: true }); - - assert.equal(client.handler.lastStatus, 202); - - const files = await waitUntil(async () => - { - const result = await client.getPlaylistFiles(0); - return result.length === 3 ? result : null; + + test('add playlist full', async () => { + await client.addPlaylist({ title: 'My playlist', index: 0, setCurrent: true }); + + const playlists = await client.getPlaylists(); + assert.equal(playlists.length, 2); + + const playlist = playlists[0]; + assert.equal(typeof playlist.id, 'string'); + delete playlist.id; + + assert.deepEqual(playlist, { + index: 0, + title: 'My playlist', + isCurrent: true, + itemCount: 0, + totalTime: 0, + }); + }); + + test('set current playlist', async () => { + await client.addPlaylist(); + await client.setCurrentPlaylist(1); + + const playlists = await client.getPlaylists(); + assert.equal(playlists[0].isCurrent, false); + assert.equal(playlists[1].isCurrent, true); }); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3], true)); -}); + test('remove playlist', async () => { + await client.addPlaylist(); + let playlists = await client.getPlaylists(); -q.test('sort playlist items asc', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2, tracks.t1, tracks.t3]); - await client.sortPlaylistItems(0, { by: '%tracknumber%' }); + const id = playlists[1].id; + await client.removePlaylist(id); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3])); -}); + playlists = await client.getPlaylists(); + assert.equal(playlists.findIndex(i => i.id === id), -1); + }); -q.test('sort playlist items desc', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2, tracks.t1, tracks.t3]); - await client.sortPlaylistItems(0, { by: '%tracknumber%', desc: true }); + test('move playlist', async () => { + await client.addPlaylist(); + await client.addPlaylist(); + let playlists = await client.getPlaylists(); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t3, tracks.t2, tracks.t1])); -}); + const id1 = playlists[1].id; + const id2 = playlists[2].id; -q.test('sort playlist items random', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); - const initialFiles = await client.getPlaylistFiles(0); + await client.movePlaylist(id1, 2); + + playlists = await client.getPlaylists(); + assert.equal(playlists[1].id, id2); + assert.equal(playlists[2].id, id1); + }); + + test('clear playlist', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.clearPlaylist(0); - for (let i = 0; i < 10; i++) - { - await client.sortPlaylistItems(0, { random: true }); const files = await client.getPlaylistFiles(0); + assert.deepEqual(files, []); + }); - if (!pathCollectionsEqual(initialFiles, files)) - { - assert.ok(true); - return; - } - } + test('rename playlist', async () => { + await client.renamePlaylist(0, 'My cool list'); + const playlists = await client.getPlaylists(); + assert.equal(playlists[0].title, 'My cool list'); + }); + + test('add playlist items single', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1])); + }); + + test('add playlist items url', async () => { + const streamUrl = 'https://upload.wikimedia.org/wikipedia/en/d/d0/Rick_Astley_-_Never_Gonna_Give_You_Up.ogg'; + + await client.addPlaylistItems(0, [streamUrl]); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [streamUrl])); + }); + + test('add playlist items multiple', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3], true)); + }); - assert.ok(false, 'expected to sort playlist randomly'); -}); + test('add playlist items to position', async () => { + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t1], { index: 0 }); + await client.addPlaylistItems(0, [tracks.t3], { index: 1 }); -q.test('remove playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t3, tracks.t2])); + }); - await client.removePlaylistItems(0, [0, 2]); + test('add playlist items async', async () => { + await client.addPlaylistItems( + 0, [tracks.t1, tracks.t2, tracks.t3], { async: true }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t2])); -}); + assert.equal(client.handler.lastStatus, 202); -q.test('move playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); + const files = await waitUntil(async () => { + const result = await client.getPlaylistFiles(0); + return result.length === 3 ? result : null; + }); - await client.movePlaylistItems({ - playlist: 0, - items: [1, 2], - targetIndex: 0 + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3], true)); }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t2, tracks.t3, tracks.t1])); -}); - -q.test('move playlist items between playlists', async assert => -{ - await client.addPlaylist(); - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); - await client.addPlaylistItems(1, [tracks.t3]); - await client.movePlaylistItems({ - from: 0, - to: 1, - items: [1, 2] + test('sort playlist items asc', async () => { + await client.addPlaylistItems(0, [tracks.t2, tracks.t1, tracks.t3]); + await client.sortPlaylistItems(0, { by: '%tracknumber%' }); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2, tracks.t3])); }); - const files1 = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files1, [tracks.t1])); - - const files2 = await client.getPlaylistFiles(1); - assert.ok(pathCollectionsEqual(files2, [tracks.t3, tracks.t2, tracks.t3])); -}); - -q.test('copy playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); - await client.copyPlaylistItems({ - playlist: 0, - items: [0, 1], - targetIndex: 1 + test('sort playlist items desc', async () => { + await client.addPlaylistItems(0, [tracks.t2, tracks.t1, tracks.t3]); + await client.sortPlaylistItems(0, { by: '%tracknumber%', desc: true }); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t3, tracks.t2, tracks.t1])); }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t1, tracks.t2, tracks.t2, tracks.t3])); -}); - -q.test('copy playlist items between playlists', async assert => -{ - await client.addPlaylist(); - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.addPlaylistItems(0, [tracks.t3]); - await client.addPlaylistItems(1, [tracks.t3]); - await client.copyPlaylistItems({ - from: 0, - to: 1, - items: [0, 1] + test('sort playlist items random', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + const initialFiles = await client.getPlaylistFiles(0); + + for (let i = 0; i < 10; i++) + { + await client.sortPlaylistItems(0, { random: true }); + const files = await client.getPlaylistFiles(0); + + if (!pathCollectionsEqual(initialFiles, files)) + { + assert.ok(true); + return; + } + } + + assert.ok(false, 'expected to sort playlist randomly'); }); - const files1 = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files1, [tracks.t1, tracks.t2, tracks.t3])); + test('remove playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); - const files2 = await client.getPlaylistFiles(1); - assert.ok(pathCollectionsEqual(files2, [tracks.t3, tracks.t1, tracks.t2])); -}); + await client.removePlaylistItems(0, [0, 2]); -q.test('replace empty playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1], {replace: true}); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t2])); + }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1])); -}); + test('move playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); -q.test('replace non-empty playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2], {replace: true}); + await client.movePlaylistItems({ + playlist: 0, + items: [1, 2], + targetIndex: 0 + }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t2])); -}); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t2, tracks.t3, tracks.t1])); + }); -q.test('add items to empty playlist and play', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1], {play: true}); + test('move playlist items between playlists', async () => { + await client.addPlaylist(); + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); + await client.addPlaylistItems(1, [tracks.t3]); + await client.movePlaylistItems({ + from: 0, + to: 1, + items: [1, 2] + }); + + const files1 = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files1, [tracks.t1])); + + const files2 = await client.getPlaylistFiles(1); + assert.ok(pathCollectionsEqual(files2, [tracks.t3, tracks.t2, tracks.t3])); + }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1])); + test('copy playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); + await client.copyPlaylistItems({ + playlist: 0, + items: [0, 1], + targetIndex: 1 + }); - await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); -}); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t1, tracks.t2, tracks.t2, tracks.t3])); + }); -q.test('add items to non-empty playlist and play', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2], {play: true}); + test('copy playlist items between playlists', async () => { + await client.addPlaylist(); + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.addPlaylistItems(0, [tracks.t3]); + await client.addPlaylistItems(1, [tracks.t3]); + await client.copyPlaylistItems({ + from: 0, + to: 1, + items: [0, 1] + }); + + const files1 = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files1, [tracks.t1, tracks.t2, tracks.t3])); + + const files2 = await client.getPlaylistFiles(1); + assert.ok(pathCollectionsEqual(files2, [tracks.t3, tracks.t1, tracks.t2])); + }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2])); + test('replace empty playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1], { replace: true }); - await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 1); -}); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1])); + }); -q.test('replace items in empty playlist and play', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1], {play: true, replace: true}); + test('replace non-empty playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2], { replace: true }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t1])); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t2])); + }); + + test('add items to empty playlist and play', async () => { + await client.addPlaylistItems(0, [tracks.t1], { play: true }); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1])); - await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); -}); + await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); + }); -q.test('replace items in non-empty playlist and play', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2], {play: true, replace: true}); + test('add items to non-empty playlist and play', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2], { play: true }); - const files = await client.getPlaylistFiles(0); - assert.ok(pathCollectionsEqual(files, [tracks.t2])); + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1, tracks.t2])); - await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); -}); + await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 1); + }); -q.test('stop playback when adding empty dir', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.play(0, 0); - await client.waitForState('playing'); - await client.addPlaylistItems(0, [tracks.emptyDir], {play: true}); - await client.waitForState('stopped'); - assert.ok(true); -}); + test('replace items in empty playlist and play', async () => { + await client.addPlaylistItems(0, [tracks.t1], { play: true, replace: true }); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t1])); + + await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); + }); + + test('replace items in non-empty playlist and play', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2], { play: true, replace: true }); + + const files = await client.getPlaylistFiles(0); + assert.ok(pathCollectionsEqual(files, [tracks.t2])); + + await client.waitForState(s => s.playbackState === 'playing' && s.activeItem.index === 0); + }); + + test('stop playback when adding empty dir', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); + await client.addPlaylistItems(0, [tracks.emptyDir], { play: true }); + await client.waitForState('stopped'); + assert.ok(true); + }); +}); \ No newline at end of file diff --git a/js/api_tests/src/query_api_tests.js b/js/api_tests/src/query_api_tests.js index 516fb89b..3dd16661 100644 --- a/js/api_tests/src/query_api_tests.js +++ b/js/api_tests/src/query_api_tests.js @@ -1,341 +1,322 @@ -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, tracks, outputConfigs, usePlayer } from './test_env.js'; -q.module('query api', usePlayer()); +describe('query api', () => { + usePlayer(); -q.test('query player', async assert => -{ - await client.addPlaylistItems(0, [tracks.t3]); + test('query player', async () => { + await client.addPlaylistItems(0, [tracks.t3]); - await client.play(0, 0); - await client.waitForState('playing'); + await client.play(0, 0); + await client.waitForState('playing'); - await client.pause(); - await client.waitForState('paused'); + await client.pause(); + await client.waitForState('paused'); - const columns = ['%title%']; - const player = await client.getPlayerState(columns); - const result = await client.query({ player: true, trcolumns: columns }); + const columns = ['%title%']; + const player = await client.getPlayerState(columns); + const result = await client.query({ player: true, trcolumns: columns }); - assert.deepEqual(result.player, player); -}); + assert.deepEqual(result.player, player); + }); + + test('query playlists', async () => { + await client.addPlaylist({ title: 'My playlist' }); + + const playlists = await client.getPlaylists(); + const result = await client.query({ playlists: true }); + + assert.deepEqual(result.playlists, playlists); + }); + + test('query playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t2, tracks.t3]); + + const columns = ['%title%']; + const playlistItems = await client.getPlaylistItems(0, columns); + + const result = await client.query({ + playlistItems: true, + plref: 0, + plcolumns: columns, + }); + + assert.deepEqual(result.playlistItems, playlistItems); + }); + + test('query outputs', async () => { + const queue = await client.getOutputs(); + const result = await client.query({ outputs: true }); + assert.deepEqual(result.outputs, queue); + }); + + test('query all', async () => { + await client.addPlaylistItems(0, [tracks.t2, tracks.t3]); + await client.addToPlayQueue(0, 1); + + await client.play(0, 0); + await client.waitForState('playing'); + + await client.pause(); + await client.waitForState('paused'); + + await client.addPlaylist({ title: 'My playlist' }); + + const columns = ['%title%']; + + const expected = { + player: await client.getPlayerState(columns), + playlists: await client.getPlaylists(), + playlistItems: await client.getPlaylistItems(0, columns), + playQueue: await client.getPlayQueue(), + outputs: await client.getOutputs(), + }; + + const result = await client.query({ + player: true, + trcolumns: columns, + playlists: true, + playlistItems: true, + plref: 0, + plcolumns: columns, + playQueue: true, + outputs: true, + }); -q.test('query playlists', async assert => -{ - await client.addPlaylist({ title: 'My playlist' }); + assert.deepEqual(result, expected); + }); + + test('expect player events', async () => { + await client.addPlaylistItems(0, [tracks.t1]); - const playlists = await client.getPlaylists(); - const result = await client.query({ playlists: true }); + const expectation = client.expectEvent({ player: true }, e => e.player === true); - assert.deepEqual(result.playlists, playlists); -}); + await expectation.ready; + await client.play(0, 0); + await expectation.done; -q.test('query playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2, tracks.t3]); + assert.ok(true); + }); - const columns = ['%title%']; - const playlistItems = await client.getPlaylistItems(0, columns); + test('expect playlist events', async () => { + const expectation = client.expectEvent({ playlists: true }, e => e.playlists === true); - const result = await client.query({ - playlistItems: true, - plref: 0, - plcolumns: columns, + await expectation.ready; + await client.addPlaylist(); + await expectation.done; + + assert.ok(true); }); - assert.deepEqual(result.playlistItems, playlistItems); -}); - -q.test('query outputs', async assert => -{ - const queue = await client.getOutputs(); - const result = await client.query({ outputs: true }); - assert.deepEqual(result.outputs, queue); -}); - -q.test('query all', async assert => -{ - await client.addPlaylistItems(0, [tracks.t2, tracks.t3]); - await client.addToPlayQueue(0, 1); - - await client.play(0, 0); - await client.waitForState('playing'); - - await client.pause(); - await client.waitForState('paused'); - - await client.addPlaylist({ title: 'My playlist' }); - - const columns = ['%title%']; - - const expected = { - player: await client.getPlayerState(columns), - playlists: await client.getPlaylists(), - playlistItems: await client.getPlaylistItems(0, columns), - playQueue: await client.getPlayQueue(), - outputs: await client.getOutputs(), - }; - - const result = await client.query({ - player: true, - trcolumns: columns, - playlists: true, - playlistItems: true, - plref: 0, - plcolumns: columns, - playQueue: true, - outputs: true, + test('expect playlist items events', async () => { + const expectation = client.expectEvent({ playlistItems: true }, e => e.playlistItems === true); + + await expectation.ready; + await client.addPlaylistItems(0, [tracks.t1]); + await expectation.done; + + assert.ok(true); }); - assert.deepEqual(result, expected); -}); + test('expect play queue events', async () => { + await client.addPlaylistItems(0, [tracks.t1]); -q.test('expect player events', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + const expectation = client.expectEvent({ playQueue: true }, e => e.playQueue === true); - const expectation = client.expectEvent({ player: true }, e => e.player === true); + await expectation.ready; + await client.addToPlayQueue(0, 0); + await expectation.done; - await expectation.ready; - await client.play(0, 0); - await expectation.done; + assert.ok(true); + }); - assert.ok(true); -}); + test('expect output config events', async () => { + const expectation = client.expectEvent({ outputs: true }, e => e.outputs === true); + await expectation.ready; + await client.setOutputDevice(outputConfigs.alternate[0].typeId, outputConfigs.alternate[0].deviceId); + await expectation.done; + assert.ok(true); + }); -q.test('expect playlist events', async assert => -{ - const expectation = client.expectEvent({ playlists: true }, e => e.playlists === true); + test('expect player updates', async () => { + await client.addPlaylistItems(0, [tracks.t1]); - await expectation.ready; - await client.addPlaylist(); - await expectation.done; + const expectation = client.expectUpdate({ player: true }, e => typeof e.player === 'object'); - assert.ok(true); -}); + await expectation.ready; + await client.play(0, 0); + await expectation.done; -q.test('expect playlist items events', async assert => -{ - const expectation = client.expectEvent({ playlistItems: true }, e => e.playlistItems === true); + const expected = await client.getPlayerState(); + delete expected.activeItem.position; - await expectation.ready; - await client.addPlaylistItems(0, [tracks.t1]); - await expectation.done; + const actual = expectation.lastEvent.player; + delete actual.activeItem.position; - assert.ok(true); -}); + assert.deepEqual(actual, expected); + }); -q.test('expect play queue events', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + test('expect playlist updates', async () => { + const expectation = client.expectUpdate({ playlists: true }, e => typeof e.playlists === 'object'); - const expectation = client.expectEvent({ playQueue: true }, e => e.playQueue === true); + await expectation.ready; + await client.addPlaylist({ title: 'My list' }); + await expectation.done; - await expectation.ready; - await client.addToPlayQueue(0, 0); - await expectation.done; + const expected = await client.getPlaylists(); + const actual = expectation.lastEvent.playlists; - assert.ok(true); -}); + assert.deepEqual(actual, expected); + }); -q.test('expect output config events', async assert => -{ - const expectation = client.expectEvent({ outputs: true }, e => e.outputs === true); - await expectation.ready; - await client.setOutputDevice(outputConfigs.alternate[0].typeId, outputConfigs.alternate[0].deviceId); - await expectation.done; - assert.ok(true); -}); + test('expect playlist items updates when adding items', async () => { + const columns = ['%title%']; + const options = { + playlistItems: true, + plref: 0, + plcolumns: columns, + }; -q.test('expect player updates', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + const expectation = client.expectUpdate( + options, e => typeof e.playlistItems === 'object'); - const expectation = client.expectUpdate({ player: true }, e => typeof e.player === 'object'); + await expectation.ready; + await client.addPlaylistItems(0, [tracks.t1]); + await expectation.done; - await expectation.ready; - await client.play(0, 0); - await expectation.done; + const expected = await client.getPlaylistItems(0, columns); + const actual = expectation.lastEvent.playlistItems; - const expected = await client.getPlayerState(); - delete expected.activeItem.position; + assert.deepEqual(actual, expected); + }); - const actual = expectation.lastEvent.player; - delete actual.activeItem.position; + test('expect playlist items updates when removing items', async () => { + const columns = ['%title%']; + const options = { + playlistItems: true, + plref: 0, + plcolumns: columns, + }; - assert.deepEqual(actual, expected); -}); + await client.addPlaylistItems(0, [tracks.t1, tracks.t2]); -q.test('expect playlist updates', async assert => -{ - const expectation = client.expectUpdate({ playlists: true }, e => typeof e.playlists === 'object'); + const expectation = client.expectUpdate( + options, e => typeof e.playlistItems === 'object'); - await expectation.ready; - await client.addPlaylist({ title: 'My list' }); - await expectation.done; + await expectation.ready; + await client.removePlaylistItems(0, [0]); + await expectation.done; - const expected = await client.getPlaylists(); - const actual = expectation.lastEvent.playlists; + const expected = await client.getPlaylistItems(0, columns); + const actual = expectation.lastEvent.playlistItems; - assert.deepEqual(actual, expected); -}); + assert.deepEqual(actual, expected); + }); -q.test('expect playlist items updates when adding items', async assert => -{ - const columns = ['%title%']; - const options = { - playlistItems: true, - plref: 0, - plcolumns: columns, - }; + test('expect volume updates', async () => { + const { volume } = await client.getPlayerState(); - const expectation = client.expectUpdate( - options, e => typeof e.playlistItems === 'object'); + const targetVolume = (volume.min + volume.max) / 2; - await expectation.ready; - await client.addPlaylistItems(0, [tracks.t1]); - await expectation.done; + const expectation = client.expectUpdate( + { player: true }, + e => e.player && e.player.volume.value === targetVolume); - const expected = await client.getPlaylistItems(0, columns); - const actual = expectation.lastEvent.playlistItems; + await expectation.ready; + await client.setVolume(targetVolume); + await expectation.done; - assert.deepEqual(actual, expected); -}); + assert.ok(true); + }); -q.test('expect playlist items updates when removing items', async assert => -{ - const columns = ['%title%']; - const options = { - playlistItems: true, - plref: 0, - plcolumns: columns, - }; + test('expect mute state updates', async () => { + const expectation = client.expectUpdate( + { player: true }, + e => e.player && e.player.volume.isMuted); - await client.addPlaylistItems(0, [tracks.t1, tracks.t2]); + await expectation.ready; + await client.setMuted(true); + await expectation.done; - const expectation = client.expectUpdate( - options, e => typeof e.playlistItems === 'object'); + assert.ok(true); + }); - await expectation.ready; - await client.removePlaylistItems(0, [0]); - await expectation.done; + test('expect active item updates when sorting playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.addPlaylistItems(0, [tracks.t2]); + await client.play(0, 0); + await client.waitForState('playing'); - const expected = await client.getPlaylistItems(0, columns); - const actual = expectation.lastEvent.playlistItems; + const expectation = client.expectUpdate( + { player: true }, + e => e.player && e.player.activeItem.index === 1); - assert.deepEqual(actual, expected); -}); + await expectation.ready; + await client.sortPlaylistItems(0, { by: '%tracknumber%', desc: true }); + await expectation.done; -q.test('expect volume updates', async assert => -{ - const { volume } = await client.getPlayerState(); + assert.ok(true); + }); - const targetVolume = (volume.min + volume.max) / 2; - - const expectation = client.expectUpdate( - { player: true }, - e => e.player && e.player.volume.value === targetVolume); - - await expectation.ready; - await client.setVolume(targetVolume); - await expectation.done; - - assert.ok(true); -}); + test('expect active item updates when adding playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1]); + await client.play(0, 0); + await client.waitForState('playing'); -q.test('expect mute state updates', async assert => -{ - const expectation = client.expectUpdate( - { player: true }, - e => e.player && e.player.volume.isMuted); + const expectation = client.expectUpdate( + { player: true }, + e => e.player && e.player.activeItem.index === 1); - await expectation.ready; - await client.setMuted(true); - await expectation.done; + await expectation.ready; + await client.addPlaylistItems(0, [tracks.t2], { index: 0 }); + await expectation.done; - assert.ok(true); -}); + assert.ok(true); + }); -q.test('expect active item updates when sorting playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.addPlaylistItems(0, [tracks.t2]); - await client.play(0, 0); - await client.waitForState('playing'); + test('expect active item updates when removing playlist items', async () => { + await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); + await client.play(0, 2); + await client.waitForState('playing'); - const expectation = client.expectUpdate( - { player: true }, - e => e.player && e.player.activeItem.index === 1); - - await expectation.ready; - await client.sortPlaylistItems(0, { by: '%tracknumber%', desc: true }); - await expectation.done; - - assert.ok(true); -}); + const expectation = client.expectUpdate( + { player: true }, + e => e.player && e.player.activeItem.index === 1); -q.test('expect active item updates when adding playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); - await client.play(0, 0); - await client.waitForState('playing'); - - const expectation = client.expectUpdate( - { player: true }, - e => e.player && e.player.activeItem.index === 1); - - await expectation.ready; - await client.addPlaylistItems(0, [tracks.t2], { index: 0 }); - await expectation.done; + await expectation.ready; + await client.removePlaylistItems(0, [0]); + await expectation.done; - assert.ok(true); -}); + assert.ok(true); + }); -q.test('expect active item updates when removing playlist items', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1, tracks.t2, tracks.t3]); - await client.play(0, 2); - await client.waitForState('playing'); + test('expect play queue updates', async () => { + await client.addPlaylistItems(0, [tracks.t1]); - const expectation = client.expectUpdate( - { player: true }, - e => e.player && e.player.activeItem.index === 1); + const columns = ['%artist%', '%title%']; + const expectation = client.expectUpdate( + { playQueue: true, qcolumns: columns }, + e => typeof e.playQueue === 'object' && e.playQueue.length > 0); - await expectation.ready; - await client.removePlaylistItems(0, [0]); - await expectation.done; + await expectation.ready; + await client.addToPlayQueue(0, 0); + await expectation.done; - assert.ok(true); -}); + const expected = await client.getPlayQueue(columns); + const actual = expectation.lastEvent.playQueue; -q.test('expect play queue updates', async assert => -{ - await client.addPlaylistItems(0, [tracks.t1]); + assert.deepEqual(actual, expected); + }); - const columns = ['%artist%', '%title%']; - const expectation = client.expectUpdate( - { playQueue: true, qcolumns: columns }, - e => typeof e.playQueue === 'object' && e.playQueue.length > 0); + test('expect output config updates', async () => { + const expectation = client.expectUpdate({ outputs: true }, e => typeof e.outputs === 'object'); + await expectation.ready; + await client.setOutputDevice(outputConfigs.alternate[0].typeId, outputConfigs.alternate[0].deviceId); + await expectation.done; - await expectation.ready; - await client.addToPlayQueue(0, 0); - await expectation.done; + const expected = await client.getOutputs(); + const actual = expectation.lastEvent.outputs; - const expected = await client.getPlayQueue(columns); - const actual = expectation.lastEvent.playQueue; - - assert.deepEqual(actual, expected); -}); - -q.test('expect output config updates', async assert => -{ - const expectation = client.expectUpdate({ outputs: true }, e => typeof e.outputs === 'object'); - await expectation.ready; - await client.setOutputDevice(outputConfigs.alternate[0].typeId, outputConfigs.alternate[0].deviceId); - await expectation.done; - - const expected = await client.getOutputs(); - const actual = expectation.lastEvent.outputs; - - assert.deepEqual(actual, expected); -}); + assert.deepEqual(actual, expected); + }); +}); \ No newline at end of file diff --git a/js/api_tests/src/static_files_tests.js b/js/api_tests/src/static_files_tests.js index 7447ea8b..48479a45 100644 --- a/js/api_tests/src/static_files_tests.js +++ b/js/api_tests/src/static_files_tests.js @@ -1,10 +1,9 @@ import path from 'path'; -import { promisify } from 'util'; import fs from 'fs'; -import q from 'qunit'; +import { describe, test, assert } from 'vitest'; import { client, config, usePlayer } from './test_env.js'; -const readFile = promisify(fs.readFile); +const readFile = fs.promises.readFile; const pluginSettings = { urlMappings: { @@ -28,221 +27,199 @@ function assertRedirect(assert, result, location) assert.equal(result.headers["location"], location); } -q.module('static files', usePlayer({ pluginSettings, axiosConfig })); +describe('static files', () => { + usePlayer({ pluginSettings, axiosConfig }); -function getFile(url, config) -{ - return client.handler.axios.get(url, config); -} - -function getFileData(url) -{ - return readFile(path.join(config.webRootDir, url), 'utf8'); -} + function getFile(url, config) + { + return client.handler.axios.get(url, config); + } -q.test('get index of root', async assert => -{ - const result = await getFile('/'); - assert.equal(result.data, 'index.html\n'); -}); + function getFileData(url) + { + return readFile(path.join(config.webRootDir, url), 'utf8'); + } -q.test('get index of prefix', async assert => -{ - const result = await getFile('/prefix/'); - assert.equal(result.data, 'index.html\n'); -}); + test('get index of root', async () => { + const result = await getFile('/'); + assert.equal(result.data, 'index.html\n'); + }); -q.test('get index of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested/'); - assert.equal(result.data, 'index.html\n'); -}); + test('get index of prefix', async () => { + const result = await getFile('/prefix/'); + assert.equal(result.data, 'index.html\n'); + }); -q.test('redirect index of prefix', async assert => -{ - const result = await getFile('/prefix'); - assert.equal(result.headers["location"], '/prefix/'); -}); + test('get index of nested prefix', async () => { + const result = await getFile('/prefix/nested/'); + assert.equal(result.data, 'index.html\n'); + }); -q.test('redirect index of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested'); - assertRedirect(assert, result, '/prefix/nested/'); -}); + test('redirect index of prefix', async () => { + const result = await getFile('/prefix'); + assert.equal(result.headers["location"], '/prefix/'); + }); -q.test('get file of root', async assert => -{ - const result = await getFile('/file.html'); - assert.equal(result.data, 'file.html\n'); -}); + test('redirect index of nested prefix', async () => { + const result = await getFile('/prefix/nested'); + assertRedirect(assert, result, '/prefix/nested/'); + }); -q.test('get file of prefix', async assert => -{ - const result = await getFile('/prefix/file.html'); - assert.equal(result.data, 'file.html\n'); -}); + test('get file of root', async () => { + const result = await getFile('/file.html'); + assert.equal(result.data, 'file.html\n'); + }); -q.test('get file of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested/file.html'); - assert.equal(result.data, 'file.html\n'); -}); + test('get file of prefix', async () => { + const result = await getFile('/prefix/file.html'); + assert.equal(result.data, 'file.html\n'); + }); -q.test('get subdir index of root', async assert => -{ - const result = await getFile('/subdir/'); - assert.equal(result.data, 'subdir/index.html\n'); -}); + test('get file of nested prefix', async () => { + const result = await getFile('/prefix/nested/file.html'); + assert.equal(result.data, 'file.html\n'); + }); -q.test('get subdir index of prefix', async assert => -{ - const result = await getFile('/prefix/subdir/'); - assert.equal(result.data, 'subdir/index.html\n'); -}); + test('get subdir index of root', async () => { + const result = await getFile('/subdir/'); + assert.equal(result.data, 'subdir/index.html\n'); + }); -q.test('get subdir index of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested/subdir/'); - assert.equal(result.data, 'subdir/index.html\n'); -}); + test('get subdir index of prefix', async () => { + const result = await getFile('/prefix/subdir/'); + assert.equal(result.data, 'subdir/index.html\n'); + }); -q.test('redirect subdir index of root', async assert => -{ - const result = await getFile('/subdir'); - assertRedirect(assert, result, '/subdir/'); -}); + test('get subdir index of nested prefix', async () => { + const result = await getFile('/prefix/nested/subdir/'); + assert.equal(result.data, 'subdir/index.html\n'); + }); -q.test('redirect subdir index of prefix', async assert => -{ - const result = await getFile('/prefix/subdir'); - assertRedirect(assert, result, '/prefix/subdir/'); -}); + test('redirect subdir index of root', async () => { + const result = await getFile('/subdir'); + assertRedirect(assert, result, '/subdir/'); + }); -q.test('redirect subdir index of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested/subdir'); - assertRedirect(assert, result, '/prefix/nested/subdir/'); -}); + test('redirect subdir index of prefix', async () => { + const result = await getFile('/prefix/subdir'); + assertRedirect(assert, result, '/prefix/subdir/'); + }); -q.test('get subdir file of root', async assert => -{ - const result = await getFile('/subdir/file.html'); - assert.equal(result.data, 'subdir/file.html\n'); -}); + test('redirect subdir index of nested prefix', async () => { + const result = await getFile('/prefix/nested/subdir'); + assertRedirect(assert, result, '/prefix/nested/subdir/'); + }); -q.test('get subdir file of prefix', async assert => -{ - const result = await getFile('/prefix/subdir/file.html'); - assert.equal(result.data, 'subdir/file.html\n'); -}); + test('get subdir file of root', async () => { + const result = await getFile('/subdir/file.html'); + assert.equal(result.data, 'subdir/file.html\n'); + }); -q.test('get subdir file of nested prefix', async assert => -{ - const result = await getFile('/prefix/nested/subdir/file.html'); - assert.equal(result.data, 'subdir/file.html\n'); -}); + test('get subdir file of prefix', async () => { + const result = await getFile('/prefix/subdir/file.html'); + assert.equal(result.data, 'subdir/file.html\n'); + }); -q.test('provide content type', async assert => -{ - const contentTypes = { - 'file.html': 'text/html; charset=utf-8', - 'file.htm': 'text/html; charset=utf-8', - 'file.css': 'text/css', - 'file.svg': 'image/svg+xml', - 'file.js': 'application/javascript', - 'file.png': 'image/png', - 'file.jpeg': 'image/jpeg', - 'file.jpg': 'image/jpeg', - 'file.txt': 'text/plain; charset=utf-8', - }; - - for (let file of Object.keys(contentTypes)) - { - const result = await getFile(file); - assert.equal(result.headers['content-type'], contentTypes[file]); - } -}); + test('get subdir file of nested prefix', async () => { + const result = await getFile('/prefix/nested/subdir/file.html'); + assert.equal(result.data, 'subdir/file.html\n'); + }); -q.test('etag support', async assert => -{ - const initialResult = await getFile('file.html'); + test('provide content type', async () => { + const contentTypes = { + 'file.html': 'text/html; charset=utf-8', + 'file.htm': 'text/html; charset=utf-8', + 'file.css': 'text/css', + 'file.svg': 'image/svg+xml', + 'file.js': 'application/javascript', + 'file.png': 'image/png', + 'file.jpeg': 'image/jpeg', + 'file.jpg': 'image/jpeg', + 'file.txt': 'text/plain; charset=utf-8', + }; + + for (let file of Object.keys(contentTypes)) + { + const result = await getFile(file); + assert.equal(result.headers['content-type'], contentTypes[file]); + } + }); - const etag = initialResult.headers['etag']; - assert.ok(etag); + test('etag support', async () => { + const initialResult = await getFile('file.html'); - const cacheControl = initialResult.headers['cache-control']; - assert.equal(cacheControl, 'max-age=3, must-revalidate'); + const etag = initialResult.headers['etag']; + assert.ok(etag); - const cachedResult = await getFile('file.html', { - headers: { 'If-None-Match': etag }, - validateStatus: () => true, - }); + const cacheControl = initialResult.headers['cache-control']; + assert.equal(cacheControl, 'max-age=3, must-revalidate'); - assert.equal(cachedResult.status, 304); - assert.equal(cachedResult.headers['etag'], etag); - assert.equal(cachedResult.headers['cache-control'], cacheControl); -}); + const cachedResult = await getFile('file.html', { + headers: { 'If-None-Match': etag }, + validateStatus: () => true, + }); -q.test('enable compression', async assert => -{ - const expectedData = await getFileData('large.txt'); - const result = await getFile('large.txt', { - headers: { 'Accept-Encoding': 'whatever, gzip' }, + assert.equal(cachedResult.status, 304); + assert.equal(cachedResult.headers['etag'], etag); + assert.equal(cachedResult.headers['cache-control'], cacheControl); }); - assert.equal(result.data, expectedData); + test('enable compression', async () => { + const expectedData = await getFileData('large.txt'); + const result = await getFile('large.txt', { + headers: { 'Accept-Encoding': 'whatever, gzip' }, + }); - const contentLength = parseInt(result.headers['content-length']); - assert.ok(contentLength < expectedData.length); -}); + assert.equal(result.data, expectedData); -q.test('disable compression', async assert => -{ - const expectedData = await getFileData('large.txt'); - const result = await getFile('large.txt', { - headers: { 'Accept-Encoding': 'whatever' }, + const contentLength = parseInt(result.headers['content-length']); + assert.ok(contentLength < expectedData.length); }); - assert.equal(result.data, expectedData); + test('disable compression', async () => { + const expectedData = await getFileData('large.txt'); + const result = await getFile('large.txt', { + headers: { 'Accept-Encoding': 'whatever' }, + }); - const contentLength = parseInt(result.headers['content-length']); - assert.equal(contentLength, expectedData.length); -}); + assert.equal(result.data, expectedData); -q.test('get via parent directory', async assert => -{ - const expectedData = await getFileData('file.txt'); + const contentLength = parseInt(result.headers['content-length']); + assert.equal(contentLength, expectedData.length); + }); - const result1 = await getFile('/test/../file.txt'); - assert.equal(result1.data, expectedData); + test('get via parent directory', async () => { + const expectedData = await getFileData('file.txt'); - const result2 = await getFile('/test/%2E%2E/file.txt'); - assert.equal(result2.data, expectedData); -}); + const result1 = await getFile('/test/../file.txt'); + assert.equal(result1.data, expectedData); -q.test('get non-existing file', async assert => -{ - const result = await getFile('/non-existing.html', ignoreStatus); - assert.equal(result.status, 404); -}); + const result2 = await getFile('/test/%2E%2E/file.txt'); + assert.equal(result2.data, expectedData); + }); -q.test('escape root dir', async assert => -{ - const result1 = await getFile('/../package.json', ignoreStatus); - assert.equal(result1.status, 404); + test('get non-existing file', async () => { + const result = await getFile('/non-existing.html', ignoreStatus); + assert.equal(result.status, 404); + }); - const result2 = await getFile('/%2E%2E/package.json', ignoreStatus); - assert.equal(result2.status, 404); + test('escape root dir', async () => { + const result1 = await getFile('/../package.json', ignoreStatus); + assert.equal(result1.status, 404); - const result3 = await getFile('/prefix/../package.json', ignoreStatus); - assert.equal(result3.status, 404); + const result2 = await getFile('/%2E%2E/package.json', ignoreStatus); + assert.equal(result2.status, 404); - const result4 = await getFile('/prefix/%2E%2E/package.json', ignoreStatus); - assert.equal(result4.status, 404); + const result3 = await getFile('/prefix/../package.json', ignoreStatus); + assert.equal(result3.status, 404); - const result5 = await getFile('/prefix/nested/../package.json', ignoreStatus); - assert.equal(result3.status, 404); + const result4 = await getFile('/prefix/%2E%2E/package.json', ignoreStatus); + assert.equal(result4.status, 404); - const result6 = await getFile('/prefix/nested/%2E%2E/package.json', ignoreStatus); - assert.equal(result4.status, 404); + const result5 = await getFile('/prefix/nested/../package.json', ignoreStatus); + assert.equal(result5.status, 404); + + const result6 = await getFile('/prefix/nested/%2E%2E/package.json', ignoreStatus); + assert.equal(result6.status, 404); + }); }); diff --git a/js/api_tests/src/test_env.js b/js/api_tests/src/test_env.js index a0cbe5d6..28d305f4 100644 --- a/js/api_tests/src/test_env.js +++ b/js/api_tests/src/test_env.js @@ -1,7 +1,7 @@ import { Foobar2000TestContextFactory } from './test_context_foobar2000.js' import { DeadbeefTestContextFactory } from './test_context_deadbeef.js' import { selectBySystem } from './utils.js'; -import qunit from 'qunit' +import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; function getContextFactory() { @@ -33,24 +33,8 @@ export const outputConfigs = context.outputConfigs; export function usePlayer(options) { - return { - before: () => context.beginSuite(options), - after: () => context.endSuite(), - beforeEach: () => context.beginTest(), - afterEach: () => context.endTest(), - }; + beforeAll(() => context.beginSuite(options)); + afterAll(() => context.endSuite()); + beforeEach(() => context.beginTest()); + afterEach(() => context.endTest()); } - -const maxFailedTests = 10; -let failedTests = 0; - -qunit.on('testEnd', event => { - if (event.status !== 'failed') - return; - - if (++failedTests < maxFailedTests) - return; - - console.error('Too many test failures, stopping'); - qunit.config.queue.length = 0; -}); diff --git a/js/api_tests/vitest.config.js b/js/api_tests/vitest.config.js new file mode 100644 index 00000000..11ef06dc --- /dev/null +++ b/js/api_tests/vitest.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['./src/*_tests.js'], + bail: 10, + retry: 1, + maxConcurrency: 1, + fileParallelism: false, + testTimeout: 10000, + teardownTimeout: 10000, + } +}); diff --git a/js/yarn.lock b/js/yarn.lock index 236c1f66..196d5f8a 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -290,6 +290,136 @@ dependencies: tslib "^2.0.0" +"@esbuild/aix-ppc64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz#521cbd968dcf362094034947f76fa1b18d2d403c" + integrity sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw== + +"@esbuild/android-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz#61ea550962d8aa12a9b33194394e007657a6df57" + integrity sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA== + +"@esbuild/android-arm@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.2.tgz#554887821e009dd6d853f972fde6c5143f1de142" + integrity sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA== + +"@esbuild/android-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.2.tgz#a7ce9d0721825fc578f9292a76d9e53334480ba2" + integrity sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A== + +"@esbuild/darwin-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz#2cb7659bd5d109803c593cfc414450d5430c8256" + integrity sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg== + +"@esbuild/darwin-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz#e741fa6b1abb0cd0364126ba34ca17fd5e7bf509" + integrity sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA== + +"@esbuild/freebsd-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz#2b64e7116865ca172d4ce034114c21f3c93e397c" + integrity sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g== + +"@esbuild/freebsd-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz#e5252551e66f499e4934efb611812f3820e990bb" + integrity sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA== + +"@esbuild/linux-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz#dc4acf235531cd6984f5d6c3b13dbfb7ddb303cb" + integrity sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw== + +"@esbuild/linux-arm@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz#56a900e39240d7d5d1d273bc053daa295c92e322" + integrity sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw== + +"@esbuild/linux-ia32@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz#d4a36d473360f6870efcd19d52bbfff59a2ed1cc" + integrity sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w== + +"@esbuild/linux-loong64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz#fcf0ab8c3eaaf45891d0195d4961cb18b579716a" + integrity sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg== + +"@esbuild/linux-mips64el@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz#598b67d34048bb7ee1901cb12e2a0a434c381c10" + integrity sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw== + +"@esbuild/linux-ppc64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz#3846c5df6b2016dab9bc95dde26c40f11e43b4c0" + integrity sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ== + +"@esbuild/linux-riscv64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz#173d4475b37c8d2c3e1707e068c174bb3f53d07d" + integrity sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA== + +"@esbuild/linux-s390x@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz#f7a4790105edcab8a5a31df26fbfac1aa3dacfab" + integrity sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w== + +"@esbuild/linux-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz#2ecc1284b1904aeb41e54c9ddc7fcd349b18f650" + integrity sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA== + +"@esbuild/netbsd-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz#e2863c2cd1501845995cb11adf26f7fe4be527b0" + integrity sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw== + +"@esbuild/netbsd-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz#93f7609e2885d1c0b5a1417885fba8d1fcc41272" + integrity sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA== + +"@esbuild/openbsd-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz#a1985604a203cdc325fd47542e106fafd698f02e" + integrity sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA== + +"@esbuild/openbsd-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz#8209e46c42f1ffbe6e4ef77a32e1f47d404ad42a" + integrity sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg== + +"@esbuild/openharmony-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz#8fade4441893d9cc44cbd7dcf3776f508ab6fb2f" + integrity sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag== + +"@esbuild/sunos-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz#980d4b9703a16f0f07016632424fc6d9a789dfc2" + integrity sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg== + +"@esbuild/win32-arm64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz#1c09a3633c949ead3d808ba37276883e71f6111a" + integrity sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg== + +"@esbuild/win32-ia32@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz#1b1e3a63ad4bef82200fef4e369e0fff7009eee5" + integrity sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ== + +"@esbuild/win32-x64@0.27.2": + version "0.27.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz#9e585ab6086bef994c6e8a5b3a0481219ada862b" + integrity sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ== + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -348,6 +478,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -369,11 +504,154 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@rollup/rollup-android-arm-eabi@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.2.tgz#60a7889627edae1e6fade79fe188db8ead2c6829" + integrity sha512-21J6xzayjy3O6NdnlO6aXi/urvSRjm6nCI6+nF6ra2YofKruGixN9kfT+dt55HVNwfDmpDHJcaS3JuP/boNnlA== + +"@rollup/rollup-android-arm64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.2.tgz#c2d15e2c1b720ea6bbcbdc6bd22fbc663840b82b" + integrity sha512-eXBg7ibkNUZ+sTwbFiDKou0BAckeV6kIigK7y5Ko4mB/5A1KLhuzEKovsmfvsL8mQorkoincMFGnQuIT92SKqA== + +"@rollup/rollup-darwin-arm64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.2.tgz#6f30bf301c6b4155f753231d220d47efe78ab04f" + integrity sha512-UCbaTklREjrc5U47ypLulAgg4njaqfOVLU18VrCrI+6E5MQjuG0lSWaqLlAJwsD7NpFV249XgB0Bi37Zh5Sz4g== + +"@rollup/rollup-darwin-x64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.2.tgz#4c9f37c97f93af9187c3cd0223b05d4f3f1eddc7" + integrity sha512-dP67MA0cCMHFT2g5XyjtpVOtp7y4UyUxN3dhLdt11at5cPKnSm4lY+EhwNvDXIMzAMIo2KU+mc9wxaAQJTn7sQ== + +"@rollup/rollup-freebsd-arm64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.2.tgz#811bf4aeb619dc834837a10bc55fb2d23622bdb2" + integrity sha512-WDUPLUwfYV9G1yxNRJdXcvISW15mpvod1Wv3ok+Ws93w1HjIVmCIFxsG2DquO+3usMNCpJQ0wqO+3GhFdl6Fow== + +"@rollup/rollup-freebsd-x64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.2.tgz#b2f3ee43ee13aa98abf30cce8a8e1f5cfc712317" + integrity sha512-Ng95wtHVEulRwn7R0tMrlUuiLVL/HXA8Lt/MYVpy88+s5ikpntzZba1qEulTuPnPIZuOPcW9wNEiqvZxZmgmqQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.2.tgz#0f7a59cef492b9d9dc225bb3d65d9638d371bc39" + integrity sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw== + +"@rollup/rollup-linux-arm-musleabihf@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.2.tgz#3b97d6d4b64d328da78a0d7d29b2783c83315dc5" + integrity sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q== + +"@rollup/rollup-linux-arm64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.2.tgz#62a49932e0210b25c85408076243d717e3efabf0" + integrity sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA== + +"@rollup/rollup-linux-arm64-musl@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.2.tgz#69cb1d164c9cde8ceae026b333bf227ea4a7ea34" + integrity sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA== + +"@rollup/rollup-linux-loong64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.2.tgz#40d8ab4dae850555fed866bd2e7218aff7fe3ccf" + integrity sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA== + +"@rollup/rollup-linux-loong64-musl@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.2.tgz#a1a7a06dbcbf9d3038df1603d7e7d2eb9bf20e6b" + integrity sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA== + +"@rollup/rollup-linux-ppc64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.2.tgz#9495db6fe330cdcc2aea781434406fd08a180442" + integrity sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw== + +"@rollup/rollup-linux-ppc64-musl@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.2.tgz#6ea3814aacddd8c811542e1a5cbd5772b9f19cce" + integrity sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ== + +"@rollup/rollup-linux-riscv64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.2.tgz#b5ea2c1599140d3cca2490c668ade233bc6c6a78" + integrity sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg== + +"@rollup/rollup-linux-riscv64-musl@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.2.tgz#3b2694f588c4eeaeab30f697ba35e17347536c53" + integrity sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q== + +"@rollup/rollup-linux-s390x-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.2.tgz#c37296f3b4642fe834c5390efeb9b85c166ac1a8" + integrity sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ== + +"@rollup/rollup-linux-x64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.2.tgz#95d926276df80cd738f4a1a7fc5b897534fc81bb" + integrity sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw== + +"@rollup/rollup-linux-x64-musl@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.2.tgz#9116cd2892f79c843f28c5045a2fc3c77204a20d" + integrity sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA== + +"@rollup/rollup-openbsd-x64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.2.tgz#d7e0517290503243d1856d27d48abadcdbc301b6" + integrity sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw== + +"@rollup/rollup-openharmony-arm64@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.2.tgz#661320edb00150f9ec9810d776225d48f0b97a33" + integrity sha512-sZnyUgGkuzIXaK3jNMPmUIyJrxu/PjmATQrocpGA1WbCPX8H5tfGgRSuYtqBYAvLuIGp8SPRb1O4d1Fkb5fXaQ== + +"@rollup/rollup-win32-arm64-msvc@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.2.tgz#38bb3e21bae763166da6992e22e413c6e5fdf957" + integrity sha512-sDpFbenhmWjNcEbBcoTV0PWvW5rPJFvu+P7XoTY0YLGRupgLbFY0XPfwIbJOObzO7QgkRDANh65RjhPmgSaAjQ== + +"@rollup/rollup-win32-ia32-msvc@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.2.tgz#fc59f6fa03cf1e87b3a60a9f1f60f8e7f676f96f" + integrity sha512-GvJ03TqqaweWCigtKQVBErw2bEhu1tyfNQbarwr94wCGnczA9HF8wqEe3U/Lfu6EdeNP0p6R+APeHVwEqVxpUQ== + +"@rollup/rollup-win32-x64-gnu@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.2.tgz#ed3f1546fce1a6918ed950aba4d1fd524c24a09c" + integrity sha512-KvXsBvp13oZz9JGe5NYS7FNizLe99Ny+W8ETsuCyjXiKdiGrcz2/J/N8qxZ/RSwivqjQguug07NLHqrIHrqfYw== + +"@rollup/rollup-win32-x64-msvc@4.55.2": + version "4.55.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.2.tgz#af3ff15decd9050692c989f9328f7808c5ec72eb" + integrity sha512-xNO+fksQhsAckRtDSPWaMeT1uIM+JrDRXlerpnWNXhn1TdB3YZ6uKBMBTKP0eX9XtYEP978hHk1f8332i2AW8Q== + +"@standard-schema/spec@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/chai@^5.2.2": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" + integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== + dependencies: + "@types/deep-eql" "*" + assertion-error "^2.0.1" + +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -395,6 +673,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== +"@types/estree@1.0.8", "@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/estree@^0.0.51": version "0.0.51" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" @@ -415,6 +698,64 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== +"@vitest/expect@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.17.tgz#67bb0d4a7d37054590a19dcf831f7936d14a8a02" + integrity sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ== + dependencies: + "@standard-schema/spec" "^1.0.0" + "@types/chai" "^5.2.2" + "@vitest/spy" "4.0.17" + "@vitest/utils" "4.0.17" + chai "^6.2.1" + tinyrainbow "^3.0.3" + +"@vitest/mocker@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.0.17.tgz#ce559098be1ae18ae5aa441a79939bcd7fc8f42f" + integrity sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ== + dependencies: + "@vitest/spy" "4.0.17" + estree-walker "^3.0.3" + magic-string "^0.30.21" + +"@vitest/pretty-format@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.0.17.tgz#dde7cb2c01699d0943571137d1b482edff5fc000" + integrity sha512-Ah3VAYmjcEdHg6+MwFE17qyLqBHZ+ni2ScKCiW2XrlSBV4H3Z7vYfPfz7CWQ33gyu76oc0Ai36+kgLU3rfF4nw== + dependencies: + tinyrainbow "^3.0.3" + +"@vitest/runner@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.0.17.tgz#dcc3bb4a4b1077858f3b105e91b2eeb208cee780" + integrity sha512-JmuQyf8aMWoo/LmNFppdpkfRVHJcsgzkbCA+/Bk7VfNH7RE6Ut2qxegeyx2j3ojtJtKIbIGy3h+KxGfYfk28YQ== + dependencies: + "@vitest/utils" "4.0.17" + pathe "^2.0.3" + +"@vitest/snapshot@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.0.17.tgz#40d71a3dad4ac39812ed96a95fded46a920e1a31" + integrity sha512-npPelD7oyL+YQM2gbIYvlavlMVWUfNNGZPcu0aEUQXt7FXTuqhmgiYupPnAanhKvyP6Srs2pIbWo30K0RbDtRQ== + dependencies: + "@vitest/pretty-format" "4.0.17" + magic-string "^0.30.21" + pathe "^2.0.3" + +"@vitest/spy@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.0.17.tgz#d0936f8908b4dae091d9b948be3bae8e19d1878d" + integrity sha512-I1bQo8QaP6tZlTomQNWKJE6ym4SHf3oLS7ceNjozxxgzavRAgZDc06T7kD8gb9bXKEgcLNt00Z+kZO6KaJ62Ew== + +"@vitest/utils@4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.0.17.tgz#48181deab273c87ac4ee20c1c454ffe9c4f453fe" + integrity sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w== + dependencies: + "@vitest/pretty-format" "4.0.17" + tinyrainbow "^3.0.3" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -636,6 +977,11 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + axios@^0.21.2: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -714,6 +1060,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== +chai@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e" + integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg== + chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1057,6 +1408,43 @@ es-module-lexer@^0.9.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +esbuild@^0.27.0: + version "0.27.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.2.tgz#d83ed2154d5813a5367376bb2292a9296fc83717" + integrity sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.2" + "@esbuild/android-arm" "0.27.2" + "@esbuild/android-arm64" "0.27.2" + "@esbuild/android-x64" "0.27.2" + "@esbuild/darwin-arm64" "0.27.2" + "@esbuild/darwin-x64" "0.27.2" + "@esbuild/freebsd-arm64" "0.27.2" + "@esbuild/freebsd-x64" "0.27.2" + "@esbuild/linux-arm" "0.27.2" + "@esbuild/linux-arm64" "0.27.2" + "@esbuild/linux-ia32" "0.27.2" + "@esbuild/linux-loong64" "0.27.2" + "@esbuild/linux-mips64el" "0.27.2" + "@esbuild/linux-ppc64" "0.27.2" + "@esbuild/linux-riscv64" "0.27.2" + "@esbuild/linux-s390x" "0.27.2" + "@esbuild/linux-x64" "0.27.2" + "@esbuild/netbsd-arm64" "0.27.2" + "@esbuild/netbsd-x64" "0.27.2" + "@esbuild/openbsd-arm64" "0.27.2" + "@esbuild/openbsd-x64" "0.27.2" + "@esbuild/openharmony-arm64" "0.27.2" + "@esbuild/sunos-x64" "0.27.2" + "@esbuild/win32-arm64" "0.27.2" + "@esbuild/win32-ia32" "0.27.2" + "@esbuild/win32-x64" "0.27.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1092,6 +1480,13 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -1107,6 +1502,11 @@ exenv@^1.2.0: resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== +expect-type@^1.2.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68" + integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1122,6 +1522,11 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -1157,6 +1562,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -1481,6 +1891,13 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -1557,6 +1974,11 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" @@ -1621,6 +2043,11 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +obug@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/obug/-/obug-2.1.1.tgz#2cba74ff241beb77d63055ddf4cd1e9f90b538be" + integrity sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1698,11 +2125,21 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" @@ -1967,6 +2404,15 @@ postcss@^8.3.5, postcss@^8.4.19: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -2098,6 +2544,40 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup@^4.43.0: + version "4.55.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.55.2.tgz#fc1cd147b1ea72b62072fb12df5e6ea28a2c1c7b" + integrity sha512-PggGy4dhwx5qaW+CKBilA/98Ql9keyfnb7lh4SR6shQ91QQQi1ORJ1v4UinkdP2i87OBs9AQFooQylcrrRfIcg== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.55.2" + "@rollup/rollup-android-arm64" "4.55.2" + "@rollup/rollup-darwin-arm64" "4.55.2" + "@rollup/rollup-darwin-x64" "4.55.2" + "@rollup/rollup-freebsd-arm64" "4.55.2" + "@rollup/rollup-freebsd-x64" "4.55.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.55.2" + "@rollup/rollup-linux-arm-musleabihf" "4.55.2" + "@rollup/rollup-linux-arm64-gnu" "4.55.2" + "@rollup/rollup-linux-arm64-musl" "4.55.2" + "@rollup/rollup-linux-loong64-gnu" "4.55.2" + "@rollup/rollup-linux-loong64-musl" "4.55.2" + "@rollup/rollup-linux-ppc64-gnu" "4.55.2" + "@rollup/rollup-linux-ppc64-musl" "4.55.2" + "@rollup/rollup-linux-riscv64-gnu" "4.55.2" + "@rollup/rollup-linux-riscv64-musl" "4.55.2" + "@rollup/rollup-linux-s390x-gnu" "4.55.2" + "@rollup/rollup-linux-x64-gnu" "4.55.2" + "@rollup/rollup-linux-x64-musl" "4.55.2" + "@rollup/rollup-openbsd-x64" "4.55.2" + "@rollup/rollup-openharmony-arm64" "4.55.2" + "@rollup/rollup-win32-arm64-msvc" "4.55.2" + "@rollup/rollup-win32-ia32-msvc" "4.55.2" + "@rollup/rollup-win32-x64-gnu" "4.55.2" + "@rollup/rollup-win32-x64-msvc" "4.55.2" + fsevents "~2.3.2" + safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -2194,6 +2674,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + sirv@^1.0.7: version "1.0.19" resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" @@ -2208,6 +2693,11 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -2226,6 +2716,16 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" + integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2340,6 +2840,29 @@ tiny-glob@0.2.9: globalyzer "0.1.0" globrex "^0.1.2" +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.2.tgz#bdd2737fe2ba40bd6f918ae26642f264b99ca251" + integrity sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== + +tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +tinyrainbow@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz#984a5b1c1b25854a9b6bccbe77964d0593d1ea42" + integrity sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -2394,6 +2917,46 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== +"vite@^6.0.0 || ^7.0.0": + version "7.3.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.1.tgz#7f6cfe8fb9074138605e822a75d9d30b814d6507" + integrity sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA== + dependencies: + esbuild "^0.27.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^4.0.17: + version "4.0.17" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.0.17.tgz#0e39e67a909a132afe434ee1278bdcf0c17fd063" + integrity sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg== + dependencies: + "@vitest/expect" "4.0.17" + "@vitest/mocker" "4.0.17" + "@vitest/pretty-format" "4.0.17" + "@vitest/runner" "4.0.17" + "@vitest/snapshot" "4.0.17" + "@vitest/spy" "4.0.17" + "@vitest/utils" "4.0.17" + es-module-lexer "^1.7.0" + expect-type "^1.2.2" + magic-string "^0.30.21" + obug "^2.1.1" + pathe "^2.0.3" + picomatch "^4.0.3" + std-env "^3.10.0" + tinybench "^2.9.0" + tinyexec "^1.0.2" + tinyglobby "^0.2.15" + tinyrainbow "^3.0.3" + vite "^6.0.0 || ^7.0.0" + why-is-node-running "^2.3.0" + warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" @@ -2492,6 +3055,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" From 8d8dc165dc63942b295e9e0bc67f85bc1e2b9a18 Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 21:18:37 +0500 Subject: [PATCH 3/7] js: refresh sources --- js/api_tests/sources.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/js/api_tests/sources.cmake b/js/api_tests/sources.cmake index 27ec57d2..efc49bb1 100644 --- a/js/api_tests/sources.cmake +++ b/js/api_tests/sources.cmake @@ -10,7 +10,6 @@ src/client_config_api_tests.js src/event_expectation.js src/http_features_tests.js src/install_app.js -src/main.js src/outputs_api_tests.js src/permissions_tests.js src/play_queue_api_tests.js From 0c70f7c4c4f42aebfde3ba45de6f0088e2fef6fe Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 21:31:58 +0500 Subject: [PATCH 4/7] api_tests: rename usePlayer to setupPlayer --- js/api_tests/src/artwork_api_tests.js | 8 ++++---- js/api_tests/src/authentication_tests.js | 4 ++-- js/api_tests/src/browser_api_tests.js | 4 ++-- js/api_tests/src/client_config_api_tests.js | 4 ++-- js/api_tests/src/http_features_tests.js | 4 ++-- js/api_tests/src/outputs_api_tests.js | 4 ++-- js/api_tests/src/permissions_tests.js | 4 ++-- js/api_tests/src/play_queue_api_tests.js | 4 ++-- js/api_tests/src/player_api_tests.js | 4 ++-- js/api_tests/src/playlists_api_tests.js | 4 ++-- js/api_tests/src/query_api_tests.js | 4 ++-- js/api_tests/src/static_files_tests.js | 4 ++-- js/api_tests/src/test_context.js | 2 +- js/api_tests/src/test_env.js | 2 +- js/api_tests/vitest.config.js | 2 +- 15 files changed, 29 insertions(+), 29 deletions(-) diff --git a/js/api_tests/src/artwork_api_tests.js b/js/api_tests/src/artwork_api_tests.js index 09517d91..8e384e42 100644 --- a/js/api_tests/src/artwork_api_tests.js +++ b/js/api_tests/src/artwork_api_tests.js @@ -1,7 +1,7 @@ import path from 'path'; import fs from 'fs'; import { describe, test, assert } from 'vitest'; -import { client, config, tracks, usePlayer } from './test_env.js'; +import { client, config, tracks, setupPlayer } from './test_env.js'; const readFile = fs.promises.readFile; @@ -27,7 +27,7 @@ function getArtwork(playlist, item) } describe('artwork 1', () => { - usePlayer(); + setupPlayer(); test('get from folder', async () => { await client.addPlaylistItems(0, [tracks.t1]); @@ -57,7 +57,7 @@ describe('artwork 1', () => { }); describe('artwork 2', () => { - usePlayer(); + setupPlayer(); test('get from tag', async () => { await client.addPlaylistItems(0, [tracks.t2Alt]); @@ -73,7 +73,7 @@ describe('artwork 2', () => { }); describe('artwork 3', () => { - usePlayer(); + setupPlayer(); test('missing', async () => { await client.addPlaylistItems(0, [tracks.t3]); diff --git a/js/api_tests/src/authentication_tests.js b/js/api_tests/src/authentication_tests.js index b71bf7fa..de2f3854 100644 --- a/js/api_tests/src/authentication_tests.js +++ b/js/api_tests/src/authentication_tests.js @@ -1,6 +1,6 @@ import { describe, test, assert } from 'vitest'; import lodash from 'lodash'; -import { client, usePlayer } from './test_env.js'; +import { client, setupPlayer } from './test_env.js'; const { startsWith } = lodash; @@ -31,7 +31,7 @@ function makeRequest(config) } describe('authentication', () => { - usePlayer({ pluginSettings, axiosConfig }); + setupPlayer({ pluginSettings, axiosConfig }); test('require auth', async () => { const response = await makeRequest(); diff --git a/js/api_tests/src/browser_api_tests.js b/js/api_tests/src/browser_api_tests.js index f40ec717..5eae8e63 100644 --- a/js/api_tests/src/browser_api_tests.js +++ b/js/api_tests/src/browser_api_tests.js @@ -2,7 +2,7 @@ import path from 'path'; import fs from 'fs'; import { omit, sortBy } from 'lodash'; import { describe, test, assert } from 'vitest'; -import { client, config, usePlayer } from './test_env.js'; +import { client, config, setupPlayer } from './test_env.js'; const readdir = fs.promises.readdir; const stat = fs.promises.stat; @@ -48,7 +48,7 @@ async function getFileSystemEntriesDirect(dirPath) } describe('browser api', () => { - usePlayer(); + setupPlayer(); test('get roots', async () => { const result = await client.getFileSystemRoots() diff --git a/js/api_tests/src/client_config_api_tests.js b/js/api_tests/src/client_config_api_tests.js index 73494f35..387f602d 100644 --- a/js/api_tests/src/client_config_api_tests.js +++ b/js/api_tests/src/client_config_api_tests.js @@ -1,8 +1,8 @@ import { describe, test, assert } from 'vitest'; -import { client, usePlayer } from './test_env.js'; +import { client, setupPlayer } from './test_env.js'; describe('client config api', () => { - usePlayer(); + setupPlayer(); test('client config', async () => { const id = 'api_test_' + Math.round(1000000 * Math.random()); diff --git a/js/api_tests/src/http_features_tests.js b/js/api_tests/src/http_features_tests.js index 0b8b2fbb..7eaf5ee5 100644 --- a/js/api_tests/src/http_features_tests.js +++ b/js/api_tests/src/http_features_tests.js @@ -1,5 +1,5 @@ import { describe, test, assert } from 'vitest'; -import { client, tracks, usePlayer } from './test_env.js'; +import { client, tracks, setupPlayer } from './test_env.js'; const expectedValue = 'Very Custom, Much Configurable, Wow'; @@ -10,7 +10,7 @@ const pluginSettings = { }; describe('http features', () => { - usePlayer({ pluginSettings }); + setupPlayer({ pluginSettings }); test('custom headers', async () => { const response = await client.handler.axios.get('/api/player'); diff --git a/js/api_tests/src/outputs_api_tests.js b/js/api_tests/src/outputs_api_tests.js index 94f2453b..b2807322 100644 --- a/js/api_tests/src/outputs_api_tests.js +++ b/js/api_tests/src/outputs_api_tests.js @@ -1,8 +1,8 @@ import { describe, test, assert } from 'vitest'; -import { client, outputConfigs, usePlayer } from './test_env.js'; +import { client, outputConfigs, setupPlayer } from './test_env.js'; describe('outputs api', () => { - usePlayer(); + setupPlayer(); test('get outputs config', async () => { const outputs = await client.getOutputs(); diff --git a/js/api_tests/src/permissions_tests.js b/js/api_tests/src/permissions_tests.js index 07d0af2f..7e543710 100644 --- a/js/api_tests/src/permissions_tests.js +++ b/js/api_tests/src/permissions_tests.js @@ -1,5 +1,5 @@ import { describe, test, assert } from 'vitest'; -import { client, outputConfigs, usePlayer } from './test_env.js'; +import { client, outputConfigs, setupPlayer } from './test_env.js'; const pluginSettings = { permissions: { @@ -24,7 +24,7 @@ function post(path, data) } describe('permissions', () => { - usePlayer({ pluginSettings, resetOptions }); + setupPlayer({ pluginSettings, resetOptions }); test('get permissions', async () => { const state = await client.getPlayerState(); diff --git a/js/api_tests/src/play_queue_api_tests.js b/js/api_tests/src/play_queue_api_tests.js index ec851f95..b2869154 100644 --- a/js/api_tests/src/play_queue_api_tests.js +++ b/js/api_tests/src/play_queue_api_tests.js @@ -1,5 +1,5 @@ import { describe, test, assert } from 'vitest'; -import { client, config, tracks, usePlayer } from './test_env.js'; +import { client, config, tracks, setupPlayer } from './test_env.js'; // DeaDBeeF clears play queue asynchronously on stop // Avoid resetting player state for stable runs @@ -8,7 +8,7 @@ const resetOptions = { }; describe('play queue api', () => { - usePlayer({ resetOptions }); + setupPlayer({ resetOptions }); async function setupTracks() { diff --git a/js/api_tests/src/player_api_tests.js b/js/api_tests/src/player_api_tests.js index ac36a8d9..011dee67 100644 --- a/js/api_tests/src/player_api_tests.js +++ b/js/api_tests/src/player_api_tests.js @@ -1,8 +1,8 @@ import { describe, test, assert } from 'vitest' -import { client, config, tracks, usePlayer } from './test_env.js'; +import { client, config, tracks, setupPlayer } from './test_env.js'; describe('player api', () => { - usePlayer(); + setupPlayer(); test('get state', async () => { const state = await client.getPlayerState(); diff --git a/js/api_tests/src/playlists_api_tests.js b/js/api_tests/src/playlists_api_tests.js index f3aa37ec..ab248432 100644 --- a/js/api_tests/src/playlists_api_tests.js +++ b/js/api_tests/src/playlists_api_tests.js @@ -1,9 +1,9 @@ import { describe, test, assert } from 'vitest'; import { pathCollectionsEqual, waitUntil } from './utils.js'; -import { client, tracks, usePlayer } from './test_env.js'; +import { client, tracks, setupPlayer } from './test_env.js'; describe('playlists api', () => { - usePlayer(); + setupPlayer(); function assertPlaylist(assert, playlist) { diff --git a/js/api_tests/src/query_api_tests.js b/js/api_tests/src/query_api_tests.js index 3dd16661..cb5bc279 100644 --- a/js/api_tests/src/query_api_tests.js +++ b/js/api_tests/src/query_api_tests.js @@ -1,8 +1,8 @@ import { describe, test, assert } from 'vitest'; -import { client, tracks, outputConfigs, usePlayer } from './test_env.js'; +import { client, tracks, outputConfigs, setupPlayer } from './test_env.js'; describe('query api', () => { - usePlayer(); + setupPlayer(); test('query player', async () => { await client.addPlaylistItems(0, [tracks.t3]); diff --git a/js/api_tests/src/static_files_tests.js b/js/api_tests/src/static_files_tests.js index 48479a45..38b5248c 100644 --- a/js/api_tests/src/static_files_tests.js +++ b/js/api_tests/src/static_files_tests.js @@ -1,7 +1,7 @@ import path from 'path'; import fs from 'fs'; import { describe, test, assert } from 'vitest'; -import { client, config, usePlayer } from './test_env.js'; +import { client, config, setupPlayer } from './test_env.js'; const readFile = fs.promises.readFile; @@ -28,7 +28,7 @@ function assertRedirect(assert, result, location) } describe('static files', () => { - usePlayer({ pluginSettings, axiosConfig }); + setupPlayer({ pluginSettings, axiosConfig }); function getFile(url, config) { diff --git a/js/api_tests/src/test_context.js b/js/api_tests/src/test_context.js index f6e26fc0..876d4dc4 100644 --- a/js/api_tests/src/test_context.js +++ b/js/api_tests/src/test_context.js @@ -3,7 +3,7 @@ import fsObj from 'fs'; import { getBuildConfig } from '../../config.mjs'; import RequestHandler from './request_handler.js'; import TestPlayerClient from './test_player_client.js'; -import { appsDir, rootDir, testsRootDir } from './utils.js'; +import { testsRootDir } from './utils.js'; const fs = fsObj.promises; diff --git a/js/api_tests/src/test_env.js b/js/api_tests/src/test_env.js index 28d305f4..a4d13045 100644 --- a/js/api_tests/src/test_env.js +++ b/js/api_tests/src/test_env.js @@ -31,7 +31,7 @@ export const config = context.config; export const client = context.client; export const outputConfigs = context.outputConfigs; -export function usePlayer(options) +export function setupPlayer(options) { beforeAll(() => context.beginSuite(options)); afterAll(() => context.endSuite()); diff --git a/js/api_tests/vitest.config.js b/js/api_tests/vitest.config.js index 11ef06dc..0184ca70 100644 --- a/js/api_tests/vitest.config.js +++ b/js/api_tests/vitest.config.js @@ -1,4 +1,4 @@ -import { defineConfig } from 'vitest/config' +import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { From 02d0077492b4dca317d26dc442f1a39bcf4205a7 Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 21:39:34 +0500 Subject: [PATCH 5/7] minor fixes --- js/api_tests/src/play_queue_api_tests.js | 37 ++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/js/api_tests/src/play_queue_api_tests.js b/js/api_tests/src/play_queue_api_tests.js index b2869154..929110ab 100644 --- a/js/api_tests/src/play_queue_api_tests.js +++ b/js/api_tests/src/play_queue_api_tests.js @@ -1,5 +1,6 @@ import { describe, test, assert } from 'vitest'; import { client, config, tracks, setupPlayer } from './test_env.js'; +import { PlayerId } from './test_context.js'; // DeaDBeeF clears play queue asynchronously on stop // Avoid resetting player state for stable runs @@ -7,24 +8,24 @@ const resetOptions = { playerState: false }; -describe('play queue api', () => { - setupPlayer({ resetOptions }); +async function setupTracks() +{ + await client.addPlaylist(); - async function setupTracks() - { - await client.addPlaylist(); + const playlists = await client.getPlaylists(); - const playlists = await client.getPlaylists(); + const p1 = playlists[0].id; + const p2 = playlists[1].id; - const p1 = playlists[0].id; - const p2 = playlists[1].id; + await client.addPlaylistItems(p1, [tracks.t1]); + await client.addPlaylistItems(p1, [tracks.t2]); + await client.addPlaylistItems(p2, [tracks.t3]); - await client.addPlaylistItems(p1, [tracks.t1]); - await client.addPlaylistItems(p1, [tracks.t2]); - await client.addPlaylistItems(p2, [tracks.t3]); + return [p1, p2]; +} - return [p1, p2]; - } +describe('play queue api', () => { + setupPlayer({ resetOptions }); test('get play queue with columns', async () => { const [p1] = await setupTracks(); @@ -38,7 +39,7 @@ describe('play queue api', () => { { playlistIndex: 0, playlistId: p1, itemIndex: 0, columns: ['Hyperblast', 'Silence Rocks - Part 1'] }, { playlistIndex: 0, playlistId: p1, itemIndex: 1, columns: ['Hyperblast', 'Silence Rocks - Part 2'] }, ]); - }) + }); test('add to queue', async () => { const [p1, p2] = await setupTracks(); @@ -54,13 +55,7 @@ describe('play queue api', () => { ]); }); - test('add to queue at index', async () => { - if (config.playerId !== 'deadbeef') - { - assert.ok('adding to queue at index is not supported by current player'); - return; - } - + test('add to queue at index', { skip: config.playerId !== PlayerId.deadbeef }, async () => { const [p1, p2] = await setupTracks(); await client.addToPlayQueue(p1, 0); From c7ed3e8d3563870e6177c99282311c54441e748a Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 21:55:50 +0500 Subject: [PATCH 6/7] remove remaining qunit usages --- js/webui/package.json | 1 - js/webui/src/tests/index.html | 11 --------- js/webui/src/tests/index.js | 7 ------ js/webui/webpack.config.js | 15 ------------- js/yarn.lock | 42 +++++------------------------------ 5 files changed, 5 insertions(+), 71 deletions(-) delete mode 100644 js/webui/src/tests/index.html delete mode 100644 js/webui/src/tests/index.js diff --git a/js/webui/package.json b/js/webui/package.json index 5625fa59..5e520f47 100644 --- a/js/webui/package.json +++ b/js/webui/package.json @@ -38,7 +38,6 @@ "less": "^4.1.3", "less-loader": "^10.2.0", "mini-css-extract-plugin": "^2.7.2", - "qunit": "^2.19.3", "style-loader": "^3.3.1", "terser-webpack-plugin": "5.3.6", "url-loader": "^4.1.1", diff --git a/js/webui/src/tests/index.html b/js/webui/src/tests/index.html deleted file mode 100644 index 7f54ccbe..00000000 --- a/js/webui/src/tests/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Tests - - -
-
- - diff --git a/js/webui/src/tests/index.js b/js/webui/src/tests/index.js deleted file mode 100644 index b54ede8b..00000000 --- a/js/webui/src/tests/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import q from 'qunit' - -q.test( "hello test", function( assert ) { - assert.ok( 1 == "1", "Passed!" ); -}); - -q.start(); diff --git a/js/webui/webpack.config.js b/js/webui/webpack.config.js index d588aad0..c6f8ba8e 100644 --- a/js/webui/webpack.config.js +++ b/js/webui/webpack.config.js @@ -73,20 +73,6 @@ function configApp(config, params) } } -function configTests(config, params) -{ - // Tests target configuration - - config.output.path = path.join(params.outputDir, 'tests'); - - config.entry.push('qunit/qunit/qunit.css'); - config.entry.push('./src/tests/index.js'); - - config.plugins.push(new HtmlPlugin({ - template: path.join(params.sourceDir, 'tests', 'index.html') - })); -} - function configSandbox(config, params) { // Sandbox target configuration @@ -227,7 +213,6 @@ export default function(env) if (params.enableTests) { - allTargets.push(makeTarget(configTests, params)); allTargets.push(makeTarget(configSandbox, params)); } diff --git a/js/yarn.lock b/js/yarn.lock index 196d5f8a..8706d363 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -1137,16 +1137,16 @@ colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -commander@7.2.0, commander@^7.0.0, commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.0.0, commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -1599,16 +1599,6 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globalyzer@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -2016,11 +2006,6 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== -node-watch@0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.7.3.tgz#6d4db88e39c8d09d3ea61d6568d80e5975abc7ab" - integrity sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ== - normalize-url@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" @@ -2440,15 +2425,6 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== -qunit@^2.19.3: - version "2.19.3" - resolved "https://registry.yarnpkg.com/qunit/-/qunit-2.19.3.tgz#bcf81a2e8d176dc19fe8dd358c4cbd08619af03a" - integrity sha512-vEnspSZ37u2oR01OA/IZ1Td5V7BvQYFECdKPv86JaBplDNa5IHg0v7jFSPoP5L5o78Dbi8sl7/ATtpRDAKlSdw== - dependencies: - commander "7.2.0" - node-watch "0.7.3" - tiny-glob "0.2.9" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -2832,14 +2808,6 @@ terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" -tiny-glob@0.2.9: - version "0.2.9" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" From fc969b794effb3be35e9b9e6d7eb698ae1df32af Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Tue, 20 Jan 2026 22:09:41 +0500 Subject: [PATCH 7/7] refresh sources --- js/webui/sources.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/webui/sources.cmake b/js/webui/sources.cmake index 83b631f5..59ff6f0e 100644 --- a/js/webui/sources.cmake +++ b/js/webui/sources.cmake @@ -63,8 +63,6 @@ src/settings_store.js src/sortable.js src/status_bar.js src/style.less -src/tests/index.html -src/tests/index.js src/timer.js src/touch_mode_controller.js src/urls.js