diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 000000000..a17d52712 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"ddc5edd3-51f2-4690-ada7-4273b04ae0c6","pid":2972655,"procStart":"43393170","acquiredAt":1778849010756} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5241e1d93..01819ebb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,11 +67,11 @@ jobs: dotnet-version: '8' - run: npm ci - run: npm run build-csharp - - run: npm run test-csharp + - run: npm run test-ci-csharp id: test - uses: actions/upload-artifact@v7 if: ${{ failure() && steps.test.conclusion == 'failure' }} - with: + with: name: test-results-csharp path: | packages/alphatab/test-data/**/*.new.png @@ -80,9 +80,11 @@ jobs: build_kotlin: name: Build and Test Kotlin runs-on: ubuntu-latest + permissions: + checks: write steps: - uses: actions/checkout@v6 - with: + with: fetch-depth: 100 - uses: actions/setup-node@v6 with: @@ -99,11 +101,17 @@ jobs: - run: npm run build-kotlin - run: npm run test-kotlin id: test + - uses: dorny/test-reporter@v3 + if: ${{ always() }} + with: + name: Kotlin Tests + path: 'packages/kotlin/**/build/test-results/**/*.xml' + reporter: java-junit - uses: actions/upload-artifact@v7 if: ${{ failure() && steps.test.conclusion == 'failure' }} - with: + with: name: test-results-kotlin path: | packages/alphatab/test-data/**/*.new.png - packages/alphatab/test-data/**/*.diff.png + packages/alphatab/test-data/**/*.diff.png - run: ./packages/kotlin/src/gradlew --stop diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..71b36c461 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "biomejs.biome", + "bierner.lit-html" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 6cf4fd946..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Extension", - "type": "extensionHost", - "request": "launch", - "autoAttachChildProcesses": true, - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}/packages/vscode/" - ] - } - ] -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c36d54203..c4aaa5ce9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@coderline/alphatab-monorepo", - "version": "1.8.2", + "version": "1.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@coderline/alphatab-monorepo", - "version": "1.8.2", + "version": "1.8.3", "workspaces": [ "packages/*" ], @@ -14,5121 +14,3402 @@ "concurrently": "^9.2.1" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", + "node_modules/@biomejs/biome": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz", + "integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "node": ">=14.21.3" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.15", + "@biomejs/cli-darwin-x64": "2.4.15", + "@biomejs/cli-linux-arm64": "2.4.15", + "@biomejs/cli-linux-arm64-musl": "2.4.15", + "@biomejs/cli-linux-x64": "2.4.15", + "@biomejs/cli-linux-x64-musl": "2.4.15", + "@biomejs/cli-win32-arm64": "2.4.15", + "@biomejs/cli-win32-x64": "2.4.15" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz", + "integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/core": { - "version": "7.28.3", + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz", + "integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=14.21.3" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz", + "integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" } }, - "node_modules/@babel/generator": { - "version": "7.28.3", + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz", + "integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz", + "integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz", + "integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz", + "integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz", + "integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", + "node_modules/@coderline/alphaskia": { + "version": "3.5.147", + "resolved": "https://registry.npmjs.org/@coderline/alphaskia/-/alphaskia-3.5.147.tgz", + "integrity": "sha512-9RDWV6cralGAvKFU/aozCPAverWcBTQJHpUh6Y7gBlggEwsVjorIp3tuzVCDfV+SqkSMsJtZOiJ9R2CdJKJIVw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "optionalDependencies": { + "@coderline/alphaskia-linux": "^3.0.0", + "@coderline/alphaskia-macos": "^3.0.0", + "@coderline/alphaskia-windows": "^3.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", + "node_modules/@coderline/alphaskia-linux": { + "version": "3.5.147", + "resolved": "https://registry.npmjs.org/@coderline/alphaskia-linux/-/alphaskia-linux-3.5.147.tgz", + "integrity": "sha512-6PoaTdAedZDsfH8JbVEH1PkxeSgQ2hNRWY5cz32QgzknOC7uXBjBD2PP3zAr3bfFj58E+mwo5R9flQtVvYv5Pg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", + "node_modules/@coderline/alphaskia-macos": { + "version": "3.5.147", + "resolved": "https://registry.npmjs.org/@coderline/alphaskia-macos/-/alphaskia-macos-3.5.147.tgz", + "integrity": "sha512-J4GQ9JZmfMnu302ouGBgvBV50AAcGwIvRSYN3awiwMh96aC+0VQZjNISxnohtUzmrotupK22L+pYVUI6YijBGA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", + "optional": true, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", + "node_modules/@coderline/alphaskia-windows": { + "version": "3.5.147", + "resolved": "https://registry.npmjs.org/@coderline/alphaskia-windows/-/alphaskia-windows-3.5.147.tgz", + "integrity": "sha512-T8G4oE2bacNuI1mDoR6l7pXpbrR4ww2tdPlshrm/8En//Y4kY0EbDRP3Vd9qr3E8HP1Zy8peMXoE0e7Rt3c4Mg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", + "node_modules/@coderline/alphatab": { + "resolved": "packages/alphatab", + "link": true + }, + "node_modules/@coderline/alphatab-alphatex": { + "resolved": "packages/alphatex", + "link": true + }, + "node_modules/@coderline/alphatab-csharp": { + "resolved": "packages/csharp", + "link": true + }, + "node_modules/@coderline/alphatab-kotlin": { + "resolved": "packages/kotlin", + "link": true + }, + "node_modules/@coderline/alphatab-language-server": { + "resolved": "packages/lsp", + "link": true + }, + "node_modules/@coderline/alphatab-monaco": { + "resolved": "packages/monaco", + "link": true + }, + "node_modules/@coderline/alphatab-playground": { + "resolved": "packages/playground", + "link": true + }, + "node_modules/@coderline/alphatab-tooling": { + "resolved": "packages/tooling", + "link": true + }, + "node_modules/@coderline/alphatab-transpiler": { + "resolved": "packages/transpiler", + "link": true + }, + "node_modules/@coderline/alphatab-vite": { + "resolved": "packages/vite", + "link": true + }, + "node_modules/@coderline/alphatab-webpack": { + "resolved": "packages/webpack", + "link": true + }, + "node_modules/@discoveryjs/json-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.0.0.tgz", + "integrity": "sha512-dDlz3W405VMFO4w5kIP9DOmELBcvFQGmLoKSdIRstBDubKFYwaNHV1NnlzMCQpXQFGWVALmeMORAuiLx18AvZQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=14.17.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.3", - "dev": true, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "license": "MIT", + "optional": true, "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "dev": true, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "license": "MIT", + "optional": true, "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "tslib": "^2.4.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/@fontsource/noto-sans": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@fontsource/noto-sans/-/noto-sans-5.2.10.tgz", + "integrity": "sha512-J58RVfS/C0Z2VBF+PoU260Tx8cdRGYuS+e3yQe4hYaIYDl0sEVn5CzlLo5zVRvQD0HaIUTV8AZMfqR7rtdEpqQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, + "node_modules/@fontsource/noto-serif": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/noto-serif/-/noto-serif-5.2.9.tgz", + "integrity": "sha512-A2b7XBo0dSxxTZg95Ap/HLinXq1pEM8PzSvbBFtPMZM5/1BLKEU1dR+PBnLixvOdcC7mup+/NjmyLryQ1E9WeA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "dev": true, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "dev": true, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", + "node_modules/@microsoft/api-extractor": { + "version": "7.58.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.7.tgz", + "integrity": "sha512-yK6OycD46gIzLRpj6ueVUWPk1ACSpkN1LBo05gY1qPTylbWyUCanXfH7+VgkI5LJrJoRSQR5F04XuCffCXLOBw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@microsoft/api-extractor-model": "7.33.8", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.1", + "@rushstack/rig-package": "0.7.3", + "@rushstack/terminal": "0.24.0", + "@rushstack/ts-command-line": "5.3.9", + "diff": "~8.0.2", + "minimatch": "10.2.3", + "resolve": "~1.22.1", + "semver": "~7.7.4", + "source-map": "~0.6.1", + "typescript": "5.9.3" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "bin": { + "api-extractor": "bin/api-extractor" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", + "node_modules/@microsoft/api-extractor-model": { + "version": "7.33.8", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.8.tgz", + "integrity": "sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.1" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "license": "MIT" }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", + "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.18.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@tybys/wasm-util": "^0.10.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "dev": true, + "node_modules/@oxc-project/types": { + "version": "0.132.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", + "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "dev": true, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "dev": true, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@babel/traverse": { - "version": "7.28.3", - "dev": true, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", - "debug": "^4.3.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@babel/types": { - "version": "7.28.2", - "dev": true, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@bcoe/v8-coverage": { + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "cpu": [ + "arm" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" - } - }, - "node_modules/@biomejs/biome": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.10.tgz", - "integrity": "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.10", - "@biomejs/cli-darwin-x64": "2.4.10", - "@biomejs/cli-linux-arm64": "2.4.10", - "@biomejs/cli-linux-arm64-musl": "2.4.10", - "@biomejs/cli-linux-x64": "2.4.10", - "@biomejs/cli-linux-x64-musl": "2.4.10", - "@biomejs/cli-win32-arm64": "2.4.10", - "@biomejs/cli-win32-x64": "2.4.10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz", - "integrity": "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", "cpu": [ "arm64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "glibc" + ], + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz", - "integrity": "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", "cpu": [ - "x64" + "arm64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "musl" + ], + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz", - "integrity": "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", "cpu": [ - "arm64" + "ppc64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "glibc" + ], + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz", - "integrity": "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", "cpu": [ - "arm64" + "s390x" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "glibc" + ], + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz", - "integrity": "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", + "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", "cpu": [ "x64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "glibc" + ], + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz", - "integrity": "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", + "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", "cpu": [ "x64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "libc": [ + "musl" + ], + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz", - "integrity": "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", "cpu": [ "arm64" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "win32" + "openharmony" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz", - "integrity": "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", "cpu": [ - "x64" + "wasm32" ], - "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=14.21.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@coderline/alphaskia": { - "version": "3.5.147", - "resolved": "https://registry.npmjs.org/@coderline/alphaskia/-/alphaskia-3.5.147.tgz", - "integrity": "sha512-9RDWV6cralGAvKFU/aozCPAverWcBTQJHpUh6Y7gBlggEwsVjorIp3tuzVCDfV+SqkSMsJtZOiJ9R2CdJKJIVw==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@coderline/alphaskia-linux": "^3.0.0", - "@coderline/alphaskia-macos": "^3.0.0", - "@coderline/alphaskia-windows": "^3.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@coderline/alphaskia-linux": { - "version": "3.4.135", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@coderline/alphaskia-macos": { - "version": "3.4.135", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "optional": true, - "engines": { - "node": ">=18.0.0" - } + "os": [ + "android" + ], + "peer": true }, - "node_modules/@coderline/alphaskia-windows": { - "version": "3.5.147", - "resolved": "https://registry.npmjs.org/@coderline/alphaskia-windows/-/alphaskia-windows-3.5.147.tgz", - "integrity": "sha512-T8G4oE2bacNuI1mDoR6l7pXpbrR4ww2tdPlshrm/8En//Y4kY0EbDRP3Vd9qr3E8HP1Zy8peMXoE0e7Rt3c4Mg==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@coderline/alphatab": { - "resolved": "packages/alphatab", - "link": true - }, - "node_modules/@coderline/alphatab-alphatex": { - "resolved": "packages/alphatex", - "link": true - }, - "node_modules/@coderline/alphatab-csharp": { - "resolved": "packages/csharp", - "link": true - }, - "node_modules/@coderline/alphatab-kotlin": { - "resolved": "packages/kotlin", - "link": true - }, - "node_modules/@coderline/alphatab-language-server": { - "resolved": "packages/lsp", - "link": true - }, - "node_modules/@coderline/alphatab-monaco": { - "resolved": "packages/monaco", - "link": true - }, - "node_modules/@coderline/alphatab-playground": { - "resolved": "packages/playground", - "link": true - }, - "node_modules/@coderline/alphatab-tooling": { - "resolved": "packages/tooling", - "link": true - }, - "node_modules/@coderline/alphatab-transpiler": { - "resolved": "packages/transpiler", - "link": true - }, - "node_modules/@coderline/alphatab-vite": { - "resolved": "packages/vite", - "link": true - }, - "node_modules/@coderline/alphatab-webpack": { - "resolved": "packages/webpack", - "link": true + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true }, - "node_modules/@discoveryjs/json-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.0.0.tgz", - "integrity": "sha512-dDlz3W405VMFO4w5kIP9DOmELBcvFQGmLoKSdIRstBDubKFYwaNHV1NnlzMCQpXQFGWVALmeMORAuiLx18AvZQ==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=14.17.0" - } + "optional": true, + "os": [ + "darwin" + ], + "peer": true }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@fontsource/noto-sans": { - "version": "5.2.10", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@fontsource/noto-serif": { - "version": "5.2.9", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@fortawesome/fontawesome-free": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.2.0.tgz", - "integrity": "sha512-3DguDv/oUE+7vjMeTSOjCSG+KeawgVQOHrKRnvUuqYh1mfArrh7s+s8hXW3e4RerBA1+Wh+hBqf8sJNpqNrBWg==", - "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", - "engines": { - "node": ">=6" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } + "peer": true }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "freebsd" + ], + "peer": true }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/@jest/snapshot-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@microsoft/api-extractor": { - "version": "7.57.7", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.7.tgz", - "integrity": "sha512-kmnmVs32MFWbV5X6BInC1/TfCs7y1ugwxv1xHsAIj/DyUfoe7vtO0alRUgbQa57+yRGHBBjlNcEk33SCAt5/dA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/api-extractor-model": "7.33.4", - "@microsoft/tsdoc": "~0.16.0", - "@microsoft/tsdoc-config": "~0.18.1", - "@rushstack/node-core-library": "5.20.3", - "@rushstack/rig-package": "0.7.2", - "@rushstack/terminal": "0.22.3", - "@rushstack/ts-command-line": "5.3.3", - "diff": "~8.0.2", - "lodash": "~4.17.23", - "minimatch": "10.2.3", - "resolve": "~1.22.1", - "semver": "~7.5.4", - "source-map": "~0.6.1", - "typescript": "5.8.2" - }, - "bin": { - "api-extractor": "bin/api-extractor" - } - }, - "node_modules/@microsoft/api-extractor-model": { - "version": "7.33.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.4.tgz", - "integrity": "sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "~0.16.0", - "@microsoft/tsdoc-config": "~0.18.1", - "@rushstack/node-core-library": "5.20.3" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/diff": { - "version": "8.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "5.8.2", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@microsoft/tsdoc": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", - "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@microsoft/tsdoc-config": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", - "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.16.0", - "ajv": "~8.18.0", - "jju": "~1.4.0", - "resolve": "~1.22.2" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", - "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "serialize-javascript": "^7.0.3", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@rollup/plugin-typescript": { - "version": "12.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.1.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.0", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.0", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rushstack/node-core-library": { - "version": "5.20.3", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.20.3.tgz", - "integrity": "sha512-95JgEPq2k7tHxhF9/OJnnyHDXfC9cLhhta0An/6MlkDsX2A6dTzDrTUG18vx4vjc280V0fi0xDH9iQczpSuWsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "~8.18.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", - "import-lazy": "~4.0.0", - "jju": "~1.4.0", - "resolve": "~1.22.1", - "semver": "~7.5.4" - }, - "peerDependencies": { - "@types/node": "*" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@rushstack/problem-matcher": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz", - "integrity": "sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/node": "*" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@rushstack/rig-package": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.2.tgz", - "integrity": "sha512-9XbFWuqMYcHUso4mnETfhGVUSaADBRj6HUAAEYk50nMPn8WRICmBuCphycQGNB3duIR6EEZX3Xj3SYc2XiP+9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "~1.22.1", - "strip-json-comments": "~3.1.1" - } - }, - "node_modules/@rushstack/terminal": { - "version": "0.22.3", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.22.3.tgz", - "integrity": "sha512-gHC9pIMrUPzAbBiI4VZMU7Q+rsCzb8hJl36lFIulIzoceKotyKL3Rd76AZ2CryCTKEg+0bnTj406HE5YY5OQvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rushstack/node-core-library": "5.20.3", - "@rushstack/problem-matcher": "0.2.1", - "supports-color": "~8.1.1" - }, - "peerDependencies": { - "@types/node": "*" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@rushstack/ts-command-line": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.3.tgz", - "integrity": "sha512-c+ltdcvC7ym+10lhwR/vWiOhsrm/bP3By2VsFcs5qTKv+6tTmxgbVrtJ5NdNjANiV5TcmOZgUN+5KYQ4llsvEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rushstack/terminal": "0.22.3", - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "string-argv": "~0.3.1" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/argparse": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/bootstrap": { - "version": "5.2.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@popperjs/core": "^2.9.2" - } - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "license": "MIT" - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "license": "MIT" - }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "license": "MIT", - "optional": true - }, - "node_modules/@types/vscode": { - "version": "1.110.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.110.0.tgz", - "integrity": "sha512-AGuxUEpU4F4mfuQjxPPaQVyuOMhs+VT/xRok1jiHVBubHK7lBRvCuOMZG0LKUwxncrPorJ5qq/uil3IdZBd5lA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vscode/test-cli": { - "version": "0.0.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mocha": "^10.0.10", - "c8": "^10.1.3", - "chokidar": "^3.6.0", - "enhanced-resolve": "^5.18.3", - "glob": "^10.3.10", - "minimatch": "^9.0.3", - "mocha": "^11.7.4", - "supports-color": "^10.2.2", - "yargs": "^17.7.2" - }, - "bin": { - "vscode-test": "out/bin.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@vscode/test-cli/node_modules/chokidar": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/@vscode/test-cli/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@vscode/test-cli/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@vscode/test-cli/node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@vscode/test-cli/node_modules/supports-color": { - "version": "10.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@vscode/test-electron": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "jszip": "^3.10.1", - "ora": "^8.1.0", - "semver": "^7.6.2" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@vscode/test-electron/node_modules/semver": { - "version": "7.7.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/alphatab-vscode": { - "resolved": "packages/vscode", - "link": true - }, - "node_modules/ansi-regex": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/assert": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", - "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", - "dev": true, - "license": "BSD-3-Clause", - "workspaces": [ - "test/babel-8" - ], - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.7", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/bootstrap": { - "version": "5.3.8", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.8" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "dev": true, - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.28.1", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "license": "MIT" - }, - "node_modules/c8": { - "version": "10.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.1", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^3.1.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "test-exclude": "^7.0.1", - "v8-to-istanbul": "^9.0.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "monocart-coverage-reports": "^2" - }, - "peerDependenciesMeta": { - "monocart-coverage-reports": { - "optional": true - } - } - }, - "node_modules/c8/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/c8/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/c8/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/c8/node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/c8/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/c8/node_modules/test-exclude": { - "version": "7.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "6.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep/node_modules/is-plain-object": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "8.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/commenting": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/concurrently": { - "version": "9.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "rxjs": "7.8.2", - "shell-quote": "1.8.3", - "supports-color": "8.1.1", - "tree-kill": "1.2.2", - "yargs": "17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/decamelize": { - "version": "4.0.0", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/deepmerge": { - "version": "4.3.1", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/define-data-property": { - "version": "1.1.4", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/define-properties": { - "version": "1.2.1", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/dom-converter": { - "version": "0.2.0", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "utila": "~0.4" - } + "optional": true, + "os": [ + "linux" + ], + "peer": true }, - "node_modules/dom-serializer": { - "version": "1.4.1", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } + "optional": true, + "os": [ + "linux" ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "4.3.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.2.7", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } + "peer": true }, - "node_modules/dot-case": { - "version": "3.0.4", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } + "optional": true, + "os": [ + "openbsd" + ], + "peer": true }, - "node_modules/dunder-proto": { - "version": "1.0.1", + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } + "optional": true, + "os": [ + "openharmony" + ], + "peer": true }, - "node_modules/eastasianwidth": { - "version": "0.2.0", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "license": "ISC" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true }, - "node_modules/emoji-regex": { - "version": "9.2.2", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "optional": true, + "os": [ + "win32" + ], + "peer": true }, - "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } + "optional": true, + "os": [ + "win32" + ], + "peer": true }, - "node_modules/entities": { - "version": "2.2.0", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true }, - "node_modules/envinfo": { - "version": "7.14.0", + "node_modules/@rushstack/node-core-library": { + "version": "5.23.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.1.tgz", + "integrity": "sha512-wlKmIKIYCKuCASbITvOxLZXepPbwXvrv7S6ig6XNWFchSyhL/E2txmVXspHY49Wu2dzf7nI27a2k/yV5BA3EiA==", "dev": true, "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" + "dependencies": { + "ajv": "~8.18.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.7.4" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/es-define-property": { - "version": "1.0.1", + "node_modules/@rushstack/problem-matcher": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz", + "integrity": "sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/es-errors": { - "version": "1.3.0", + "node_modules/@rushstack/rig-package": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.3.tgz", + "integrity": "sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "jju": "~1.4.0", + "resolve": "~1.22.1" } }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", + "node_modules/@rushstack/terminal": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.24.0.tgz", + "integrity": "sha512-8ZQS4MMaGsv27EXCBiH7WMPkRZrffeDoIevs6z9TM5dzqiY6+Hn4evfK/G+gvgBTjfvfkHIZPQQmalmI2sM4TQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.27.2", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "@rushstack/node-core-library": "5.23.1", + "@rushstack/problem-matcher": "0.2.1", + "supports-color": "~8.1.1" }, - "engines": { - "node": ">=18" + "peerDependencies": { + "@types/node": "*" }, - "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" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/escalade": { - "version": "3.2.0", + "node_modules/@rushstack/ts-command-line": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.9.tgz", + "integrity": "sha512-GIHqU+sRGQ3LGWAZu1O+9Yh++qwtyNIIGuNbcWHJjBTm2qRez0cwINUHZ+pQLR8UuzZDcMajrDaNbUYoaL/XtQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@rushstack/terminal": "0.24.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" } }, - "node_modules/escape-html": { - "version": "1.0.3", + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "license": "BSD-2-Clause", + "optional": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "tslib": "^2.4.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/esrecurse": { - "version": "4.3.0", - "license": "BSD-2-Clause", + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "node_modules/estree-walker": { - "version": "2.0.2", + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/events": { - "version": "3.3.0", + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "license": "MIT", - "engines": { - "node": ">=0.8.x" + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", - "dev": true, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" + "node_modules/@types/node": { + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "license": "MIT", - "engines": { - "node": ">= 4.9.1" + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@vitest/expect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz", + "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/fdir": { - "version": "6.5.0", + "node_modules/@vitest/mocker": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz", + "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" + "dependencies": { + "@vitest/spy": "4.1.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "picomatch": "^3 || ^4" + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "picomatch": { + "msw": { + "optional": true + }, + "vite": { "optional": true } } }, - "node_modules/fill-range": { - "version": "7.1.1", + "node_modules/@vitest/pretty-format": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz", + "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/find-up": { - "version": "4.1.0", + "node_modules/@vitest/runner": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz", + "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@vitest/utils": "4.1.7", + "pathe": "^2.0.3" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/flat": { - "version": "5.0.2", + "node_modules/@vitest/snapshot": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz", + "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==", "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.7", + "@vitest/utils": "4.1.7", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/for-each": { - "version": "0.3.5", + "node_modules/@vitest/spy": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz", + "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==", "dev": true, "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/vitest" } }, - "node_modules/foreground-child": { - "version": "3.3.1", + "node_modules/@vitest/utils": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz", + "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" + "@vitest/pretty-format": "4.1.7", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" } }, - "node_modules/fresh": { - "version": "2.0.0", + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, - "node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", - "dev": true, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "dev": true, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "dev": true, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=8.0.0" + "node": ">=0.4.0" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "dev": true, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" } }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "devOptional": true, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/glob": { - "version": "10.5.0", + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/glob-parent": { - "version": "5.1.2", + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "ajv": "^8.0.0" }, - "engines": { - "node": ">= 6" + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/globrex": { - "version": "0.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" + "color-convert": "^2.0.1" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "es-define-property": "^1.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/has-symbols": { - "version": "1.1.0", + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "sprintf-js": "~1.0.2" } }, - "node_modules/hasown": { - "version": "2.0.2", + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/he": { - "version": "1.2.0", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", - "bin": { - "he": "bin/he" + "engines": { + "node": ">=12" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz", + "integrity": "sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==", + "license": "Apache-2.0", "bin": { - "html-minifier-terser": "cli.js" + "baseline-browser-mapping": "dist/cli.cjs" }, "engines": { - "node": ">=12" + "node": ">=6.0.0" } }, - "node_modules/html-webpack-plugin": { - "version": "5.6.6", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", - "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, "engines": { - "node": ">=10.13.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/htmlparser2": { - "version": "6.1.0", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "license": "ISC" }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">= 14" + "node": "18 || 20 || >=22" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "fill-range": "^7.1.1" }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/import-local": { - "version": "3.2.0", - "dev": true, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { - "import-local-fixture": "fixtures/cli.js" + "browserslist": "cli.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" } }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "node_modules/interpret": { - "version": "3.1.1", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">=18" } }, - "node_modules/is-arguments": { - "version": "1.2.0", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/is-callable": { - "version": "1.2.7", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">= 0.4" + "node": ">= 8.10.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "dev": true, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dev": true, "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/is-generator-function": { - "version": "1.1.0", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/is-glob": { - "version": "4.0.3", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12" } }, - "node_modules/is-module": { - "version": "1.0.0", + "node_modules/commenting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", + "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", "dev": true, "license": "MIT" }, - "node_modules/is-nan": { - "version": "1.3.2", + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/is-plain-obj": { - "version": "2.1.0", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/is-regex": { - "version": "1.2.1", + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" + "node": ">= 6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "dev": true, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/isarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { + "node_modules/depd": { "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "dev": true, + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, "engines": { - "node": ">=10" + "node": ">=0.3.1" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "utila": "~0.4" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/istanbul-reports": { - "version": "3.2.0", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" }, - "node_modules/jackspeak": { - "version": "3.4.3", + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "BSD-2-Clause", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "domelementtype": "^2.2.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 4" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", - "dev": true, + "node_modules/enhanced-resolve": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "picomatch": "^4.0.3", - "walker": "^1.0.8" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "node": ">=10.13.0" } }, - "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" + "bin": { + "envinfo": "dist/cli.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", - "dev": true, - "license": "MIT", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8.0.0" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { + "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4.0" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4.0" } }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" - }, + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", - "dev": true, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-util": "30.3.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.8.x" } }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.0.0" } }, - "node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 4.9.1" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "flat": "cli.js" } }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14.14" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "ISC", "engines": { - "node": ">=8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true, - "license": "MIT" + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" }, - "node_modules/js-tokens": { + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "function-bind": "^1.1.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.4" } }, - "node_modules/jsesc": { - "version": "3.1.0", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "he": "bin/he" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, "bin": { - "json5": "lib/cli.js" + "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/html-webpack-plugin": { + "version": "5.6.7", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", + "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "dev": true, - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/lie": { - "version": "3.3.0", + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "license": "MIT", "dependencies": { - "immediate": "~3.0.5" + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" } }, - "node_modules/loader-runner": { - "version": "4.3.1", + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, "engines": { - "node": ">=6.11.5" + "node": ">= 0.8" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://opencollective.com/express" } }, - "node_modules/locate-path": { - "version": "5.0.0", + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lower-case": { - "version": "2.0.2", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/lru-cache": { - "version": "5.1.1", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "engines": { + "node": ">=8" } }, - "node_modules/make-dir": { - "version": "4.0.0", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.5.3" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marked": { - "version": "14.0.0", "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, "engines": { - "node": ">= 18" + "node": ">=0.12.0" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/merge-stream": { + "node_modules/isexe": { "version": "2.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, - "node_modules/mime-db": { - "version": "1.52.0", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 10.13.0" } }, - "node_modules/mimic-function": { - "version": "5.0.1", + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/minimatch": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", - "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/minimatch/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": ">=0.10.0" } }, - "node_modules/minimatch/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", "dependencies": { - "balanced-match": "^4.0.2" + "detect-libc": "^2.0.3" }, "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "license": "MIT", + "node": ">= 12.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha": { - "version": "11.7.5", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/moment": { - "version": "2.30.1", - "dev": true, - "license": "MIT", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "*" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/monaco-editor": { - "version": "0.55.1", - "license": "MIT", - "dependencies": { - "dompurify": "3.2.7", - "marked": "14.0.0" + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, - "node_modules/neo-async": { - "version": "2.6.2", - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, "license": "MIT", "dependencies": { - "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "license": "MIT" + "node_modules/lucide": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-1.14.0.tgz", + "integrity": "sha512-IoRC3lHwemJWvsXKcHK90hkgY4h1HGztBL63w2XwFtIu8gFDPp4/kiuqVtlN3vaM9bxsLQ4ZUBJfGsbKFaB2IA==", + "license": "ISC" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/object-is": { - "version": "1.1.6", - "dev": true, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" + "bin": { + "marked": "bin/marked.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 18" } }, - "node_modules/object.assign": { - "version": "4.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" }, - "node_modules/on-finished": { - "version": "2.4.1", + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" + "node": ">= 0.6" } }, - "node_modules/onetime": { - "version": "7.0.0", - "dev": true, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/ora": { - "version": "8.2.0", + "node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.6.0", + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "MIT" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "node_modules/ora/node_modules/is-unicode-supported": { - "version": "2.1.0", + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "dev": true, + "node_modules/monaco-editor": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", + "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", "license": "MIT", "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" + "dompurify": "3.2.7", + "marked": "14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, - "node_modules/ora/node_modules/string-width": { - "version": "7.2.0", + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" + "boolbase": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/p-locate": { - "version": "4.1.0", + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, "node_modules/p-map": { @@ -5146,6 +3427,8 @@ }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", "engines": { @@ -5154,11 +3437,15 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/package-name-regex": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz", + "integrity": "sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA==", "dev": true, "license": "MIT", "engines": { @@ -5168,13 +3455,10 @@ "url": "https://github.com/sponsors/dword-design" } }, - "node_modules/pako": { - "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" - }, "node_modules/param-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, "license": "MIT", "dependencies": { @@ -5184,6 +3468,8 @@ }, "node_modules/parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5191,6 +3477,8 @@ }, "node_modules/pascal-case": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, "license": "MIT", "dependencies": { @@ -5200,24 +3488,18 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -5226,35 +3508,28 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "license": "ISC" + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -5263,37 +3538,79 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/postcss": { - "version": "8.5.6", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -5310,7 +3627,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5320,6 +3637,8 @@ }, "node_modules/pretty-error": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, "license": "MIT", "dependencies": { @@ -5327,81 +3646,45 @@ "renderkid": "^3.0.0" } }, - "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "2.3.8", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "4.1.2", + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">=8.6" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/rechoir": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5413,6 +3696,8 @@ }, "node_modules/relateurl": { "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, "license": "MIT", "engines": { @@ -5421,6 +3706,8 @@ }, "node_modules/renderkid": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, "license": "MIT", "dependencies": { @@ -5431,27 +3718,10 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/renderkid/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/renderkid/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -5460,17 +3730,22 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.10", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -5486,6 +3761,8 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", "dependencies": { @@ -5497,35 +3774,14 @@ }, "node_modules/resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "devOptional": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/rimraf": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", @@ -5565,9 +3821,9 @@ } }, "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -5591,8 +3847,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rolldown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.132.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.2", + "@rolldown/binding-darwin-arm64": "1.0.2", + "@rolldown/binding-darwin-x64": "1.0.2", + "@rolldown/binding-freebsd-x64": "1.0.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", + "@rolldown/binding-linux-arm64-gnu": "1.0.2", + "@rolldown/binding-linux-arm64-musl": "1.0.2", + "@rolldown/binding-linux-ppc64-gnu": "1.0.2", + "@rolldown/binding-linux-s390x-gnu": "1.0.2", + "@rolldown/binding-linux-x64-gnu": "1.0.2", + "@rolldown/binding-linux-x64-musl": "1.0.2", + "@rolldown/binding-openharmony-arm64": "1.0.2", + "@rolldown/binding-wasm32-wasi": "1.0.2", + "@rolldown/binding-win32-arm64-msvc": "1.0.2", + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, "node_modules/rollup": { - "version": "4.50.0", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -5606,34 +3898,38 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.0", - "@rollup/rollup-android-arm64": "4.50.0", - "@rollup/rollup-darwin-arm64": "4.50.0", - "@rollup/rollup-darwin-x64": "4.50.0", - "@rollup/rollup-freebsd-arm64": "4.50.0", - "@rollup/rollup-freebsd-x64": "4.50.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", - "@rollup/rollup-linux-arm-musleabihf": "4.50.0", - "@rollup/rollup-linux-arm64-gnu": "4.50.0", - "@rollup/rollup-linux-arm64-musl": "4.50.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", - "@rollup/rollup-linux-ppc64-gnu": "4.50.0", - "@rollup/rollup-linux-riscv64-gnu": "4.50.0", - "@rollup/rollup-linux-riscv64-musl": "4.50.0", - "@rollup/rollup-linux-s390x-gnu": "4.50.0", - "@rollup/rollup-linux-x64-gnu": "4.50.0", - "@rollup/rollup-linux-x64-musl": "4.50.0", - "@rollup/rollup-openharmony-arm64": "4.50.0", - "@rollup/rollup-win32-arm64-msvc": "4.50.0", - "@rollup/rollup-win32-ia32-msvc": "4.50.0", - "@rollup/rollup-win32-x64-msvc": "4.50.0", + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" } }, "node_modules/rollup-plugin-license": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.7.0.tgz", - "integrity": "sha512-RvvOIF+GH3fBR3wffgc/vmjQn6qOn72WjppWVDp/v+CLpT0BbcRBdSkPeeIOL6U5XccdYgSIMjUyXgxlKEEFcw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.7.1.tgz", + "integrity": "sha512-FcGXUbAmPvRSLxjVdjp/r/MUtKBlttVQd+ApUyvKfREnsoAfAZA6Ic2fE1Tz4RL0f9XqEQU9UIRNUMdtQtliDw==", "dev": true, "license": "MIT", "dependencies": { @@ -5654,7 +3950,9 @@ } }, "node_modules/rollup-plugin-node-externals": { - "version": "8.1.2", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-9.0.1.tgz", + "integrity": "sha512-Yp91CX1ebDA8s4+BY7bOOdhF8VRR1XAKnSsHeVhYg4DW4MRXPsF4F2FnSsN9ghJgFhHl3L1uyK7A9NA/SF4Zgw==", "dev": true, "funding": [ { @@ -5668,55 +3966,31 @@ ], "license": "MIT", "engines": { - "node": ">= 21 || ^20.6.0 || ^18.19.0" + "node": ">= 24" }, "peerDependencies": { - "rollup": "^4.0.0" + "rollup": "^4.0.0", + "vite": ">=5.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "vite": { + "optional": true + } } }, "node_modules/rxjs": { "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/schema-utils": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", @@ -5754,12 +4028,11 @@ } }, "node_modules/semver": { - "version": "7.5.4", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -5767,69 +4040,46 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/send": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" - } - }, - "node_modules/send/node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" }, - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serialize-javascript": { - "version": "6.0.2", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">=20.0.0" } }, "node_modules/serve-static": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -5845,33 +4095,16 @@ "url": "https://opencollective.com/express" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "license": "MIT", "dependencies": { @@ -5883,6 +4116,8 @@ }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -5894,6 +4129,8 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -5902,6 +4139,8 @@ }, "node_modules/shell-quote": { "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -5911,34 +4150,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "4.1.0", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "ISC" }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/smob": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.1.tgz", + "integrity": "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=20.0.0" } }, - "node_modules/smob": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, "node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -5946,517 +4178,827 @@ }, "node_modules/source-map-js": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-expression-validate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz", + "integrity": "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==", + "dev": true, + "license": "(MIT AND CC-BY-3.0)", + "dependencies": { + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true, + "license": "(MIT AND CC-BY-3.0)" + }, + "node_modules/spdx-satisfies": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.1.tgz", + "integrity": "sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/split.js": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz", + "integrity": "sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "node_modules/source-map-support": { - "version": "0.5.21", + "node_modules/terser-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==", "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/spdx-compare": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-find-index": "^1.0.2", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" - } + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, - "license": "CC-BY-3.0" + "license": "MIT" }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", + "node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": ">=18" } }, - "node_modules/spdx-expression-validate": { - "version": "2.0.0", - "dev": true, - "license": "(MIT AND CC-BY-3.0)", + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", "dependencies": { - "spdx-expression-parse": "^3.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.22", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/spdx-ranges": { - "version": "2.1.1", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, - "license": "(MIT AND CC-BY-3.0)" + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/spdx-satisfies": { + "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { - "spdx-compare": "^1.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "node_modules/split.js": { - "version": "1.6.5", - "dev": true, - "license": "MIT" - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, "engines": { - "node": ">=10" + "node": ">=0.6" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "bin": { + "tree-kill": "cli.js" } }, - "node_modules/statuses": { - "version": "2.0.2", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "devOptional": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz", + "integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==", + "devOptional": true, "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.6.19" + "node": ">=18" } }, - "node_modules/string-width": { - "version": "5.1.2", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/supports-color": { - "version": "8.1.1", + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=18" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/synckit": { - "version": "0.11.11", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" + "node": ">=18" } }, - "node_modules/tapable": { - "version": "2.3.0", + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=18" } }, - "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.17", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.17.tgz", - "integrity": "sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==", + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "node": ">=18" } }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 10.13.0" + "node": ">=18" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=8.0" + "node": ">=18" } }, - "node_modules/toidentifier": { - "version": "1.0.1", + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.6" + "node": ">=18" } }, - "node_modules/tree-kill": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "dev": true, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, - "node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.21.0", + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "devOptional": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, "bin": { - "tsx": "dist/cli.mjs" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" }, "optionalDependencies": { - "fsevents": "~2.3.3" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6465,21 +5007,10 @@ "node": ">=14.17" } }, - "node_modules/uglify-js": { - "version": "3.19.3", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/universalify": { @@ -6493,7 +5024,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.2", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -6520,54 +5053,24 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/util": { - "version": "0.12.5", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/utila": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", "dev": true, "license": "MIT" }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", + "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", "license": "MIT", - "peer": true, "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" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.2", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" @@ -6583,9 +5086,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -6598,13 +5102,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -6631,9 +5138,9 @@ } }, "node_modules/vite-plugin-static-copy": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.3.0.tgz", - "integrity": "sha512-XiAtZcev7nppxNFgKoD55rfL+ukVp/RtrnTJONRwRuzv/B2FK2h2ZRCYjvxhwBV/Oarse83SiyXBSxMTfeEM0Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-4.1.0.tgz", + "integrity": "sha512-9XOarNV7LgP0KBB7AApxdgFikLXx3daZdqjC3AevYsL6MrUH62zphonLUs2a6LZc1HN1GY+vQdheZ8VVJb6dQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6643,109 +5150,119 @@ "tinyglobby": "^0.2.15" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^22.0.0 || >=24.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/sapphi-red" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/vite-plugin-static-copy/node_modules/chokidar": { - "version": "3.6.0", + "node_modules/vitest": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz", + "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" + "@vitest/expect": "4.1.7", + "@vitest/mocker": "4.1.7", + "@vitest/pretty-format": "4.1.7", + "@vitest/runner": "4.1.7", + "@vitest/snapshot": "4.1.7", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "bin": { + "vitest": "vitest.mjs" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/vite-plugin-static-copy/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", "engines": { - "node": ">=8.6" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vite-plugin-static-copy/node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.1.tgz", - "integrity": "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vite": "*" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.7", + "@vitest/browser-preview": "4.1.7", + "@vitest/browser-webdriverio": "4.1.7", + "@vitest/coverage-istanbul": "4.1.7", + "@vitest/coverage-v8": "4.1.7", + "@vitest/ui": "4.1.7", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "node_modules/vscode-languageclient": { - "version": "9.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minimatch": "^5.1.0", - "semver": "^7.3.7", - "vscode-languageserver-protocol": "3.17.5" - }, - "engines": { - "vscode": "^1.82.0" - } - }, - "node_modules/vscode-languageclient/node_modules/minimatch": { - "version": "5.1.6", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/vscode-languageserver": { "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", "license": "MIT", "dependencies": { "vscode-languageserver-protocol": "3.17.5" @@ -6756,6 +5273,8 @@ }, "node_modules/vscode-languageserver-protocol": { "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", "license": "MIT", "dependencies": { "vscode-jsonrpc": "8.2.0", @@ -6764,14 +5283,20 @@ }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", "license": "MIT" }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", "license": "MIT" }, "node_modules/vscode-oniguruma": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", + "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", "license": "MIT" }, "node_modules/vscode-textmate": { @@ -6780,16 +5305,6 @@ "integrity": "sha512-n2uGbUcrjhUEBH16uGA0TvUfhWwliFZ1e3+pTjrkim1Mt7ydB41lV08aUvsi70OlzDWp6X7Bx3w/x3fAXIsN0Q==", "license": "MIT" }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", @@ -6804,11 +5319,10 @@ } }, "node_modules/webpack": { - "version": "5.105.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", - "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -6826,9 +5340,8 @@ "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", @@ -6905,6 +5418,8 @@ }, "node_modules/webpack-merge": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", "dependencies": { @@ -6917,9 +5432,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.0.tgz", + "integrity": "sha512-gHwIe1cgBvvfLeu1Yz/dcFpmHfKDVxxyqI+kzqmuxZED81z2ChxpyqPaWcNqigPywhaEke7AjSGga+kxY55gjQ==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -6927,183 +5442,76 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -7121,141 +5529,108 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { "node": ">=12" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "packages/alphatab": { "name": "@coderline/alphatab", - "version": "1.8.2", + "version": "1.8.3", "license": "MPL-2.0", "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@coderline/alphaskia": "^3.5.147", - "@coderline/alphaskia-linux": "^3.4.135", + "@coderline/alphaskia-linux": "^3.5.147", "@coderline/alphaskia-windows": "^3.5.147", - "@types/chai": "^5.2.3", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "chalk": "^5.6.2", - "jest-snapshot": "^30.3.0", - "mocha": "^11.7.5", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vite": "^7.3.2", - "vite-plugin-static-copy": "^3.3.0" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vite": "^8.0.14", + "vite-plugin-static-copy": "^4.1.0", + "vitest": "^4.1.7" }, "engines": { "node": ">=6.0.0" } }, + "packages/alphatab/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "packages/alphatex": { "name": "@coderline/alphatab-alphatex", - "version": "1.8.2", + "version": "1.8.3", "devDependencies": { "rimraf": "^6.1.3", "tslib": "^2.8.1", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^6.0.3", + "vitest": "^4.1.6" + } + }, + "packages/alphatex/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/csharp": { "name": "@coderline/alphatab-csharp", - "version": "1.8.2", + "version": "1.8.3", "devDependencies": { "@coderline/alphatab-transpiler": "*", - "rimraf": "^6.1.3" + "rimraf": "^6.1.3", + "typescript": "^6.0.3" + } + }, + "packages/csharp/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/kotlin": { "name": "@coderline/alphatab-kotlin", - "version": "1.8.2" + "version": "1.8.3" }, "packages/lsp": { "name": "@coderline/alphatab-language-server", - "version": "1.8.2", + "version": "1.8.3", "license": "MPL-2.0", "dependencies": { - "@coderline/alphatab": "^1.8.2", + "@coderline/alphatab": "^1.8.3", "vscode-languageserver": "^9.0.1", "vscode-languageserver-textdocument": "^1.0.12" }, @@ -7263,27 +5638,37 @@ "alphatab-language-server": "dist/server.mjs" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@microsoft/api-extractor": "^7.57.7", - "@types/chai": "^5.2.2", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "mocha": "^11.7.4", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vitest": "^4.1.7" + } + }, + "packages/lsp/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/monaco": { "name": "@coderline/alphatab-monaco", - "version": "1.8.2", + "version": "1.8.3", "license": "MPL-2.0", "dependencies": { - "@coderline/alphatab": "^1.8.2", - "@coderline/alphatab-language-server": "^1.8.2", + "@coderline/alphatab": "^1.8.3", + "@coderline/alphatab-language-server": "^1.8.3", "monaco-editor": "^0.55.1", "vscode-languageserver-types": "^3.17.5", "vscode-oniguruma": "^2.0.1", @@ -7293,98 +5678,167 @@ "alphatab-monaco": "dist/alphaTab.monaco.mjs" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@microsoft/api-extractor": "^7.57.7", - "@types/chai": "^5.2.2", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "mocha": "^11.7.4", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vitest": "^4.1.6" + } + }, + "packages/monaco/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/playground": { "name": "@coderline/alphatab-playground", - "version": "1.8.2", + "version": "1.8.3", "dependencies": { "@coderline/alphatab": "*", "@fontsource/noto-sans": "^5.2.10", "@fontsource/noto-serif": "^5.2.9", - "@fortawesome/fontawesome-free": "^7.2.0", "@popperjs/core": "^2.11.8", "@types/serve-static": "^2.2.0", - "bootstrap": "^5.3.8", - "handlebars": "^4.7.9", + "lucide": "^1.14.0", "monaco-editor": "^0.55.1", "serve-static": "^2.2.1" }, "devDependencies": { - "@types/bootstrap": "^5.2.10", "split.js": "^1.6.5", "tslib": "^2.8.1", - "typescript": "^5.9.3", - "vite": "^7.3.2", - "vite-tsconfig-paths": "^6.1.1" + "typescript": "^6.0.3", + "vite": "^8.0.14", + "vitest": "^4.1.7" + } + }, + "packages/playground/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/tooling": { "name": "@coderline/alphatab-tooling", - "version": "1.8.2", + "version": "1.8.3", "devDependencies": { "@microsoft/api-extractor": "^7.57.7", "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "magic-string": "^0.30.21", "rollup-plugin-license": "^3.7.0", - "typescript": "^5.9.3" + "rollup-plugin-node-externals": "^9.0.1", + "typescript": "^6.0.3", + "vite": "^8.0.14", + "vitest": "^4.1.6" + } + }, + "packages/tooling/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/transpiler": { "name": "@coderline/alphatab-transpiler", - "version": "1.8.2" + "version": "1.8.3", + "devDependencies": { + "typescript": "^6.0.3", + "vitest": "^4.1.7" + } + }, + "packages/transpiler/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, "packages/vite": { "name": "@coderline/alphatab-vite", - "version": "1.8.2", + "version": "1.8.3", "license": "MPL-2.0", "dependencies": { - "magic-string": "^0.30.21", - "vite": "^7.3.2" + "magic-string": "^0.30.21" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@microsoft/api-extractor": "^7.57.7", - "@types/chai": "^5.2.3", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "mocha": "^11.7.5", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", - "rollup-plugin-node-externals": "^8.1.2", - "terser": "^5.46.1", + "rollup-plugin-node-externals": "^9.0.1", + "terser": "^5.47.1", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vitest": "^4.1.7" }, "engines": { "node": ">=20.19.0" + }, + "peerDependencies": { + "vite": "^7 || ^8" + } + }, + "packages/vite/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, "packages/vscode": { "name": "alphatab-vscode", - "version": "1.8.2", + "version": "1.9.0", + "extraneous": true, "license": "MPL-2.0", "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@rollup/plugin-node-resolve": "^16.0.3", "@types/chai": "^5.2.2", "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "@types/vscode": "^1.110.0", + "@types/node": "^25.6.0", + "@types/vscode": "^1.120.0", "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "assert": "^2.1.0", @@ -7403,29 +5857,39 @@ }, "packages/webpack": { "name": "@coderline/alphatab-webpack", - "version": "1.8.2", + "version": "1.8.3", "license": "MPL-2.0", "dependencies": { "webpack": "^5.105.4" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", - "@types/chai": "^5.2.3", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", + "@biomejs/biome": "^2.4.15", + "@types/node": "^25.9.1", "html-webpack-plugin": "^5.6.6", - "mocha": "^11.7.5", "rimraf": "^6.1.3", "tslib": "^2.8.1", "tsx": "^4.21.0", - "typescript": "^5.9.3", + "typescript": "^6.0.3", + "vitest": "^4.1.7", "webpack-cli": "^7.0.2" }, "engines": { "node": ">=20.19.0" } + }, + "packages/webpack/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } } } } diff --git a/package.json b/package.json index 1be632aed..d640fc0c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-monorepo", - "version": "1.8.2", + "version": "1.8.3", "description": "Monorepo for alphaTab and its related packages", "private": true, "type": "module", @@ -16,7 +16,6 @@ "build-language-server": "npm run build --workspace=packages/lsp", "dev-language-server": "npm run dev --workspace=packages/lsp", - "dev-vscode": "concurrently \"npm run dev --workspace=packages/lsp\" \"npm run dev --workspace=packages/vscode\"", "update-version": "node scripts/update-version.mjs", @@ -26,10 +25,11 @@ "test": "npm run test --workspace=packages/alphatab", "build-web": "npm run build && npm run build-webpack && npm run build-vite", - "test-web": "npm run test && npm run test-webpack && npm run test-vite", + "test-web": "npm run test-web --workspaces --if-present", "build-csharp": "npm run generate-typescript && npm run build --workspace=packages/csharp", "test-csharp": "npm run test --workspace=packages/csharp", + "test-ci-csharp": "npm run test-ci --workspace=packages/csharp", "build-kotlin": "npm run generate-typescript && npm run build --workspace=packages/kotlin", "test-kotlin": "npm run test --workspace=packages/kotlin", diff --git a/packages/alphatab/.env b/packages/alphatab/.env deleted file mode 100644 index 33ccbd705..000000000 --- a/packages/alphatab/.env +++ /dev/null @@ -1,2 +0,0 @@ -FORCE_COLOR=1 -NODE_OPTIONS=--expose-gc \ No newline at end of file diff --git a/packages/alphatab/.mocharc.json b/packages/alphatab/.mocharc.json deleted file mode 100644 index 7956a6f58..000000000 --- a/packages/alphatab/.mocharc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": [ - "ts" - ], - "node-option": [ - "env-file=.env", - "experimental-specifier-resolution=node", - "import=tsx/esm", - "no-warnings" - ], - "spec": "test/**/*.test.ts", - "timeout": "10000", - "require": [ - "test/global-hooks.ts" - ] -} \ No newline at end of file diff --git a/packages/alphatab/package.json b/packages/alphatab/package.json index df2379295..9b91bb4dc 100644 --- a/packages/alphatab/package.json +++ b/packages/alphatab/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab", - "version": "1.8.2", + "version": "1.8.3", "description": "alphaTab is a music notation and guitar tablature rendering library", "keywords": [ "guitar", @@ -54,28 +54,23 @@ "bravura-smufl": "tsx scripts/smufl-metadata.ts font/bravura/bravura_metadata.json font/bravura/bravura_alphatab_metadata.json true", "generate-typescript": "rimraf src/generated && tsx scripts/generate-typescript.ts", "build": "npm run generate-typescript && vite build --mode esm && vite build --mode umd", - "test": "mocha", + "test": "vitest run", + "test-web": "npm run test", "test-accept-reference": "tsx scripts/accept-new-reference-files.ts" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@coderline/alphaskia": "^3.5.147", - "@coderline/alphaskia-linux": "^3.4.135", + "@coderline/alphaskia-linux": "^3.5.147", "@coderline/alphaskia-windows": "^3.5.147", - "@types/chai": "^5.2.3", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "chalk": "^5.6.2", - "jest-snapshot": "^30.3.0", - "mocha": "^11.7.5", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vite": "^7.3.2", - "vite-plugin-static-copy": "^3.3.0" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vite": "^8.0.14", + "vite-plugin-static-copy": "^4.1.0", + "vitest": "^4.1.7" }, "files": [ "/dist/alphaTab*.js", diff --git a/packages/alphatab/scripts/CloneEmitter.ts b/packages/alphatab/scripts/CloneEmitter.ts index ae720a0d1..af1f1933e 100644 --- a/packages/alphatab/scripts/CloneEmitter.ts +++ b/packages/alphatab/scripts/CloneEmitter.ts @@ -329,7 +329,7 @@ function createCloneMethod( export default createEmitter('cloneable', (program, input) => { console.log(`Writing Cloner for ${input.name!.text}`); const sourceFileName = path.relative( - path.join(path.resolve(program.getCompilerOptions().baseUrl!)), + path.dirname(program.getCompilerOptions().configFilePath as string), path.resolve(input.getSourceFile().fileName) ); diff --git a/packages/alphatab/scripts/EmitterBase.ts b/packages/alphatab/scripts/EmitterBase.ts index b5dc08b1f..f38fa3a95 100644 --- a/packages/alphatab/scripts/EmitterBase.ts +++ b/packages/alphatab/scripts/EmitterBase.ts @@ -10,7 +10,7 @@ export const GENERATED_FILE_HEADER = `\ // `; export function generateFile(program: ts.Program, sourceFile: ts.SourceFile, fileName: string) { - const targetFileName = path.join(path.resolve(program.getCompilerOptions().baseUrl!), 'src/generated', fileName); + const targetFileName = path.join(path.dirname(program.getCompilerOptions().configFilePath as string), 'src/generated', fileName); fs.mkdirSync(path.dirname(targetFileName), { recursive: true }); @@ -65,7 +65,7 @@ export function generateClass( generate: (program: ts.Program, classDeclaration: ts.ClassDeclaration) => ts.SourceFile ) { const sourceFileName = path.relative( - path.resolve(program.getCompilerOptions().baseUrl!, 'src'), + path.join(path.dirname(program.getCompilerOptions().configFilePath as string), 'src'), path.resolve(classDeclaration.getSourceFile().fileName) ); diff --git a/packages/alphatab/scripts/SerializerEmitter.ts b/packages/alphatab/scripts/SerializerEmitter.ts index d60a1e589..a323bf00d 100644 --- a/packages/alphatab/scripts/SerializerEmitter.ts +++ b/packages/alphatab/scripts/SerializerEmitter.ts @@ -14,7 +14,7 @@ import { buildTypeSchema, toImportPath } from './TypeSchema'; export default createEmitter('json', (program, input) => { console.log(`Writing Serializer for ${input.name!.text}`); const sourceFileName = path.relative( - path.join(path.resolve(program.getCompilerOptions().baseUrl!)), + path.dirname(program.getCompilerOptions().configFilePath as string), path.resolve(input.getSourceFile().fileName) ); diff --git a/packages/alphatab/scripts/TypeSchema.ts b/packages/alphatab/scripts/TypeSchema.ts index 0a7e0cdbd..dce37d296 100644 --- a/packages/alphatab/scripts/TypeSchema.ts +++ b/packages/alphatab/scripts/TypeSchema.ts @@ -302,9 +302,9 @@ export function getTypeWithNullableInfo( fillBaseInfoFrom(node); } else if (node.isUnion()) { for (const t of node.types) { - if ((t.flags & ts.TypeFlags.Null) !== 0) { + if (t === checker.getNullType()) { typeInfo.isNullable = true; - } else if ((t.flags & ts.TypeFlags.Undefined) !== 0) { + } else if (t === checker.getUndefinedType()) { typeInfo.isOptional = true; } else if (!mainType) { fillBaseInfoFrom(t); @@ -407,7 +407,7 @@ function findModule(type: ts.Type, options: ts.CompilerOptions) { for (const decl of type.symbol.declarations) { const file = decl.getSourceFile(); if (file) { - const relative = path.relative(path.join(path.resolve(options.baseUrl!)), path.resolve(file.fileName)); + const relative = path.relative(path.dirname(options.configFilePath as string), path.resolve(file.fileName)); return toImportPath(relative); } } diff --git a/packages/alphatab/src/AlphaTabApiBase.ts b/packages/alphatab/src/AlphaTabApiBase.ts index b80c8f7dd..9ca2e45d4 100644 --- a/packages/alphatab/src/AlphaTabApiBase.ts +++ b/packages/alphatab/src/AlphaTabApiBase.ts @@ -2450,7 +2450,8 @@ export class AlphaTabApiBase { this._isInitialBeatCursorUpdate || barBounds.y !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.y || startBeatX < previousBeatBounds.onNotesX || - barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1; + barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1 || + barBounds.h !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.h; if (jumpCursor) { cursorHandler.placeBeatCursor(beatCursor, beatBoundings, startBeatX); @@ -3120,28 +3121,29 @@ export class AlphaTabApiBase { if (this._selectionStart && this._tickCache) { // get the start and stop ticks (which consider properly repeats) const tickCache: MidiTickLookup = this._tickCache; - const realMasterBarStart: number = tickCache.getMasterBarStart( + const realStartMasterBarStart: number = tickCache.getMasterBarStart( this._selectionStart.beat.voice.bar.masterBar ); + const startBeatPlaybackRange = tickCache.getRelativeBeatPlaybackRange(this._selectionStart.beat); + const startBeatPlaybackStart = startBeatPlaybackRange?.startTick ?? this._selectionStart.beat.playbackStart; // move to selection start this._currentBeat = null; // reset current beat so it is updating the cursor if (this._player.state === PlayerState.Paused) { - this._cursorUpdateTick(this._tickCache.getBeatStart(this._selectionStart.beat), false, 1); + this._cursorUpdateTick(realStartMasterBarStart + startBeatPlaybackStart, false, 1); } - this.tickPosition = realMasterBarStart + this._selectionStart.beat.playbackStart; + this.tickPosition = realStartMasterBarStart + startBeatPlaybackStart; // set playback range if (this._selectionEnd && this._selectionStart.beat !== this._selectionEnd.beat) { - const realMasterBarEnd: number = tickCache.getMasterBarStart( + const realEndMasterBarStart: number = tickCache.getMasterBarStart( this._selectionEnd.beat.voice.bar.masterBar ); - + const endBeatPlaybackRange = tickCache.getRelativeBeatPlaybackRange(this._selectionEnd.beat); + const endBeatPlaybackEnd = + endBeatPlaybackRange?.endTick ?? + this._selectionEnd.beat.playbackStart + this._selectionEnd.beat.playbackDuration; const range = new PlaybackRange(); - range.startTick = realMasterBarStart + this._selectionStart.beat.playbackStart; - range.endTick = - realMasterBarEnd + - this._selectionEnd.beat.playbackStart + - this._selectionEnd.beat.playbackDuration - - 50; + range.startTick = realStartMasterBarStart + startBeatPlaybackStart; + range.endTick = realEndMasterBarStart + endBeatPlaybackEnd - 50; this.playbackRange = range; } else { this._selectionStart = undefined; @@ -3621,7 +3623,7 @@ export class AlphaTabApiBase { this._currentBeat = null; this._cursorUpdateTick(this._previousTick, false, 1, true, true); - if(this._selectionStart) { + if (this._selectionStart) { this.highlightPlaybackRange(this._selectionStart.beat, this._selectionEnd!.beat); } diff --git a/packages/alphatab/src/DisplaySettings.ts b/packages/alphatab/src/DisplaySettings.ts index 3e2f526e9..4fce2772d 100644 --- a/packages/alphatab/src/DisplaySettings.ts +++ b/packages/alphatab/src/DisplaySettings.ts @@ -394,9 +394,16 @@ export class DisplaySettings { * * The page layout does not use `displayWidth`. The use of absolute widths would break the proper alignments needed for this kind of display. * - * Also note that the sizing is including any glyphs and notation elements within the bar. e.g. if there are clefs in the bar, they are still "squeezed" into the available size. - * It is not the case that the actual notes with their lengths are sized accordingly. This fits the sizing system of Guitar Pro and when files are customized there, - * alphaTab will match this layout quite close. + * In both modes, prefix and postfix glyphs (clef, key signature, time signature, barlines) are treated as fixed overhead: they keep their + * natural size and the remaining staff width is distributed across bars by a per-bar weight. This matches the convention used by + * Guitar Pro, Dorico, Finale, Sibelius and MuseScore. Bars that carry a system-start prefix or a mid-line clef/key/time-signature change + * are therefore visibly wider than plain bars with the same weight. The weight source depends on the mode: + * + * * `Automatic` (default for `page` layout): weights come from the built-in spacing engine (the natural content width of each bar). + * `displayScale` on the model is ignored. + * * `UseModelLayout` (and the `parchment` layout): weights come from `bar.displayScale` / `masterBar.displayScale`. An unset + * `displayScale` defaults to `1` and behaves identically to an explicit `1`, matching Guitar Pro (which omits the value when the + * author hasn't customized it). * * ### Horizontal Layout * diff --git a/packages/alphatab/src/ImporterSettings.ts b/packages/alphatab/src/ImporterSettings.ts index 5eb3dbafd..a6553f4b7 100644 --- a/packages/alphatab/src/ImporterSettings.ts +++ b/packages/alphatab/src/ImporterSettings.ts @@ -63,4 +63,16 @@ export class ImporterSettings { * ![Disabled](https://alphatab.net/img/reference/property/beattextaslyrics-disabled.png) */ public beatTextAsLyrics: boolean = false; + + /** + * This setting controls the escape hatch for handling potentially malicous or corrupt + * input files. At selected spots in the codebase, we use this buffer size as maximum + * allowed sizes. e.g. during unzipping or decoding strings. + * This prevents resource exhaustion, especially when alphaTab is used on server side. + * Increase this buffer size if you need to handle very big files. + * @defaultValue `128000000` + * @category Core + * @since 1.9.0 + */ + public maxDecodingBufferSize: number = 128000000; } diff --git a/packages/alphatab/src/NotationSettings.ts b/packages/alphatab/src/NotationSettings.ts index 51514e260..fcb6f41b1 100644 --- a/packages/alphatab/src/NotationSettings.ts +++ b/packages/alphatab/src/NotationSettings.ts @@ -372,7 +372,17 @@ export enum NotationElement { /** * The slurs shown on bend effects within the score staff. */ - ScoreBendSlur = 55 + ScoreBendSlur = 55, + + /** + * The hammer-on pull-off text shown on slurs. + */ + EffectHammerOnPullOffText = 56, + + /** + * The slide text shown on slurs. + */ + EffectSlideText = 57 } /** diff --git a/packages/alphatab/src/RenderingResources.ts b/packages/alphatab/src/RenderingResources.ts index df711ecd7..beb2338d0 100644 --- a/packages/alphatab/src/RenderingResources.ts +++ b/packages/alphatab/src/RenderingResources.ts @@ -53,7 +53,9 @@ export class RenderingResources { [NotationElement.RepeatCount, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)], [NotationElement.BarNumber, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)], [NotationElement.ScoreBendSlur, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)], - [NotationElement.EffectAlternateEndings, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)] + [NotationElement.EffectAlternateEndings, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)], + [NotationElement.EffectHammerOnPullOffText, RenderingResources._effectFont], + [NotationElement.EffectSlideText, RenderingResources._effectFont] ]); /** @@ -381,8 +383,16 @@ export class RenderingResources { break; } + return this.getFontForNotationElement(notationElement); + } + + /** + * @internal + * @param element + */ + public getFontForNotationElement(notationElement: NotationElement): Font { return this.elementFonts.has(notationElement) ? this.elementFonts.get(notationElement)! - : RenderingResources.defaultFonts.get(NotationElement.ScoreWords)!; + : RenderingResources.defaultFonts.get(notationElement)!; } } diff --git a/packages/alphatab/src/alphaTab.main.ts b/packages/alphatab/src/alphaTab.main.ts index e25e536ad..db18dae40 100644 --- a/packages/alphatab/src/alphaTab.main.ts +++ b/packages/alphatab/src/alphaTab.main.ts @@ -114,7 +114,10 @@ if (alphaTab.Environment.isRunningInWorker) { alphaTab.Environment.isViteBundled ) { alphaTab.Logger.debug('AlphaTab', 'Creating Module worklet'); - const alphaTabWorklet = context.audioWorklet; // this name triggers the WebPack Plugin + // destructure-rename keeps the `alphaTabWorklet` binding alive past + // rolldown's single-use inlining and isolates the call from the + // built-in bundler handlers that match on `audioWorklet.addModule`. + const { audioWorklet: alphaTabWorklet } = context; return alphaTabWorklet.addModule( new alphaTab.Environment.alphaTabUrl('./alphaTab.worklet.ts', import.meta.url) ); diff --git a/packages/alphatab/src/generated/DisplaySettingsJson.ts b/packages/alphatab/src/generated/DisplaySettingsJson.ts index 887455617..20cb54ddb 100644 --- a/packages/alphatab/src/generated/DisplaySettingsJson.ts +++ b/packages/alphatab/src/generated/DisplaySettingsJson.ts @@ -351,9 +351,16 @@ export interface DisplaySettingsJson { * * The page layout does not use `displayWidth`. The use of absolute widths would break the proper alignments needed for this kind of display. * - * Also note that the sizing is including any glyphs and notation elements within the bar. e.g. if there are clefs in the bar, they are still "squeezed" into the available size. - * It is not the case that the actual notes with their lengths are sized accordingly. This fits the sizing system of Guitar Pro and when files are customized there, - * alphaTab will match this layout quite close. + * In both modes, prefix and postfix glyphs (clef, key signature, time signature, barlines) are treated as fixed overhead: they keep their + * natural size and the remaining staff width is distributed across bars by a per-bar weight. This matches the convention used by + * Guitar Pro, Dorico, Finale, Sibelius and MuseScore. Bars that carry a system-start prefix or a mid-line clef/key/time-signature change + * are therefore visibly wider than plain bars with the same weight. The weight source depends on the mode: + * + * * `Automatic` (default for `page` layout): weights come from the built-in spacing engine (the natural content width of each bar). + * `displayScale` on the model is ignored. + * * `UseModelLayout` (and the `parchment` layout): weights come from `bar.displayScale` / `masterBar.displayScale`. An unset + * `displayScale` defaults to `1` and behaves identically to an explicit `1`, matching Guitar Pro (which omits the value when the + * author hasn't customized it). * * ### Horizontal Layout * diff --git a/packages/alphatab/src/generated/ImporterSettingsJson.ts b/packages/alphatab/src/generated/ImporterSettingsJson.ts index 30e8377d8..582768163 100644 --- a/packages/alphatab/src/generated/ImporterSettingsJson.ts +++ b/packages/alphatab/src/generated/ImporterSettingsJson.ts @@ -67,4 +67,15 @@ export interface ImporterSettingsJson { * ![Disabled](https://alphatab.net/img/reference/property/beattextaslyrics-disabled.png) */ beatTextAsLyrics?: boolean; + /** + * This setting controls the escape hatch for handling potentially malicous or corrupt + * input files. At selected spots in the codebase, we use this buffer size as maximum + * allowed sizes. e.g. during unzipping or decoding strings. + * This prevents resource exhaustion, especially when alphaTab is used on server side. + * Increase this buffer size if you need to handle very big files. + * @defaultValue `128000000` + * @category Core + * @since 1.9.0 + */ + maxDecodingBufferSize?: number; } diff --git a/packages/alphatab/src/generated/ImporterSettingsSerializer.ts b/packages/alphatab/src/generated/ImporterSettingsSerializer.ts index 5ddd11854..90545e615 100644 --- a/packages/alphatab/src/generated/ImporterSettingsSerializer.ts +++ b/packages/alphatab/src/generated/ImporterSettingsSerializer.ts @@ -23,6 +23,7 @@ export class ImporterSettingsSerializer { o.set("encoding", obj.encoding); o.set("mergepartgroupsinmusicxml", obj.mergePartGroupsInMusicXml); o.set("beattextaslyrics", obj.beatTextAsLyrics); + o.set("maxdecodingbuffersize", obj.maxDecodingBufferSize); return o; } public static setProperty(obj: ImporterSettings, property: string, v: unknown): boolean { @@ -36,6 +37,9 @@ export class ImporterSettingsSerializer { case "beattextaslyrics": obj.beatTextAsLyrics = v! as boolean; return true; + case "maxdecodingbuffersize": + obj.maxDecodingBufferSize = v! as number; + return true; } return false; } diff --git a/packages/alphatab/src/importer/BinaryStylesheet.ts b/packages/alphatab/src/importer/BinaryStylesheet.ts index 3da315306..7c19d014b 100644 --- a/packages/alphatab/src/importer/BinaryStylesheet.ts +++ b/packages/alphatab/src/importer/BinaryStylesheet.ts @@ -76,18 +76,18 @@ export class BinaryStylesheet { private readonly _types: Map = new Map(); public readonly raw: Map = new Map(); - public constructor(data?: Uint8Array) { + public constructor(data?: Uint8Array, maxDecodingBufferSize: number = 0) { if (data) { - this._read(data); + this._read(data, maxDecodingBufferSize); } } - private _read(data: Uint8Array) { + private _read(data: Uint8Array, maxDecodingBufferSize: number) { // BinaryStylesheet apears to be big-endien const readable: ByteBuffer = ByteBuffer.fromBuffer(data); const entryCount: number = IOHelper.readInt32BE(readable); for (let i: number = 0; i < entryCount; i++) { - const key: string = GpBinaryHelpers.gpReadString(readable, readable.readByte(), 'utf-8'); + const key: string = GpBinaryHelpers.gpReadString(readable, readable.readByte(), 'utf-8', maxDecodingBufferSize); const type: DataType = readable.readByte() as DataType; this._types.set(key, type); switch (type) { @@ -104,7 +104,7 @@ export class BinaryStylesheet { this.addValue(key, fvalue); break; case DataType.String: - const s: string = GpBinaryHelpers.gpReadString(readable, IOHelper.readInt16BE(readable), 'utf-8'); + const s: string = GpBinaryHelpers.gpReadString(readable, IOHelper.readInt16BE(readable), 'utf-8', maxDecodingBufferSize); this.addValue(key, s); break; case DataType.Point: diff --git a/packages/alphatab/src/importer/CapellaImporter.ts b/packages/alphatab/src/importer/CapellaImporter.ts index c380b4110..b1d4727b7 100644 --- a/packages/alphatab/src/importer/CapellaImporter.ts +++ b/packages/alphatab/src/importer/CapellaImporter.ts @@ -21,7 +21,7 @@ export class CapellaImporter extends ScoreImporter { public readScore(): Score { Logger.debug(this.name, 'Loading ZIP entries'); - const fileSystem: ZipReader = new ZipReader(this.data); + const fileSystem: ZipReader = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize); let entries: ZipEntry[]; let xml: string | null = null; entries = fileSystem.read(); diff --git a/packages/alphatab/src/importer/Gp3To5Importer.ts b/packages/alphatab/src/importer/Gp3To5Importer.ts index cc74873be..949838be2 100644 --- a/packages/alphatab/src/importer/Gp3To5Importer.ts +++ b/packages/alphatab/src/importer/Gp3To5Importer.ts @@ -4,7 +4,7 @@ import { ScoreImporter } from '@coderline/alphatab/importer/ScoreImporter'; import { UnsupportedFormatError } from '@coderline/alphatab/importer/UnsupportedFormatError'; import { IOHelper } from '@coderline/alphatab/io/IOHelper'; -import type { IReadable } from '@coderline/alphatab/io/IReadable'; +import { OverflowError, type IReadable } from '@coderline/alphatab/io/IReadable'; import { AccentuationType } from '@coderline/alphatab/model/AccentuationType'; import { Automation, AutomationType } from '@coderline/alphatab/model/Automation'; import { Bar, BarLineStyle } from '@coderline/alphatab/model/Bar'; @@ -57,7 +57,7 @@ export class Gp3To5Importer extends ScoreImporter { // NOTE: General Midi only defines percussion instruments from 35-81 // Guitar Pro 5 allowed GS extensions (27-34 and 82-87) - // GP7-8 do not have all these definitions anymore, this lookup ensures some fallback + // GP7-8 do not have all these definitions anymore, this lookup ensures some fallback // (even if they are not correct) // we can support this properly in future when we allow custom alphaTex articulation definitions // then we don't need to rely on GP specifics anymore but handle things on export/import @@ -126,7 +126,11 @@ export class Gp3To5Importer extends ScoreImporter { this._initialTempo = Automation.buildTempoAutomation(false, 0, 0, 0); if (this._versionNumber >= 500) { this.readPageSetup(); - this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); } // tempo stuff this._initialTempo.value = IOHelper.readInt32LE(this.data); @@ -170,7 +174,11 @@ export class Gp3To5Importer extends ScoreImporter { } // contents this._barCount = IOHelper.readInt32LE(this.data); + this._ensureLoopBoundary(this._barCount, Gp3To5Importer._maxBarCount, 'bar count'); + this._trackCount = IOHelper.readInt32LE(this.data); + this._ensureLoopBoundary(this._trackCount, Gp3To5Importer._maxTrackCount, 'track count'); + this.readMasterBars(); this.readTracks(); this.readBars(); @@ -224,36 +232,108 @@ export class Gp3To5Importer extends ScoreImporter { } public readScoreInformation(): void { - this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.title = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.artist = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.album = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.words = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); this._score.music = this._versionNumber >= 500 - ? GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding) + ? GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ) : this._score.words; - this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); - this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.tab = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); const noticeLines: number = IOHelper.readInt32LE(this.data); + this._ensureLoopBoundary(noticeLines, Gp3To5Importer._maxNoticeLines, 'notice line count'); let notice: string = ''; for (let i: number = 0; i < noticeLines; i++) { if (i > 0) { notice += '\r\n'; } - notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding)?.toString(); + notice += GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + )?.toString(); } this._score.notices = notice; } + // very generous thresholds for values which control loop boundaries + // prevents DoS or resource exhaustion for corrupt files or files with malicious intent + // not configurable, as realistically GP3-5 files will not exceed these values, + + // I don't hink anyone is that verbose in the small GP5 box where you can add notices + private static readonly _maxNoticeLines = 1000; + + // I haven't encountered such a long song in the wild. beyond 1000 bars something is clearly off + private static readonly _maxBarCount = 1000; + + // I think GP5 itself limits already to ~10. 100 tracks is just unrealistic, proof me wrong + private static readonly _maxTrackCount = 100; + + // nobody reallistically writes that many beats in one bar either. + private static readonly _maxBeatCount = 100; + + // I think GP5 already limits this to way less, very generous to allow 4 times more than likely the UI supports + private static readonly _maxBendPointCount = BendPoint.MaxPosition * 4; + + private _ensureLoopBoundary(value: number, maximumValue: number, label: string) { + if (value > maximumValue) { + throw new OverflowError( + `'${label}' with value ${value} has exceeded the internal safety threshold of ${maximumValue}` + ); + } + } + public readLyrics(): void { this._lyrics = []; this._lyricsTrack = IOHelper.readInt32LE(this.data) - 1; for (let i: number = 0; i < 5; i++) { const lyrics: Lyrics = new Lyrics(); lyrics.startBar = IOHelper.readInt32LE(this.data) - 1; - lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this.settings.importer.encoding); + lyrics.text = GpBinaryHelpers.gpReadStringInt( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); this._lyrics.push(lyrics); } } @@ -272,49 +352,89 @@ export class Gp3To5Importer extends ScoreImporter { ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).isVisible = (flags & (0x01 << 0)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).isVisible = (flags & (0x01 << 1)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).isVisible = (flags & (0x01 << 2)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).isVisible = (flags & (0x01 << 3)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).isVisible = (flags & (0x01 << 4)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).isVisible = (flags & (0x01 << 5)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).isVisible = (flags & (0x01 << 6)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).isVisible = (flags & (0x01 << 7)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).isVisible = (flags & (0x01 << 7)) !== 0; ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).template = - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); // page number format - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); } public readPlaybackInfos(): void { @@ -397,7 +517,11 @@ export class Gp3To5Importer extends ScoreImporter { // marker if ((flags & 0x20) !== 0) { const section: Section = new Section(); - section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + section.text = GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); section.marker = ''; GpBinaryHelpers.gpReadColor(this.data, false); newMasterBar.section = section; @@ -589,10 +713,18 @@ export class Gp3To5Importer extends ScoreImporter { this.data.skip(4); // RSE: effect name - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); // RSE: effect category - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); } } else { if (tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) { @@ -651,6 +783,8 @@ export class Gp3To5Importer extends ScoreImporter { } const newVoice: Voice = new Voice(); bar.addVoice(newVoice); + + this._ensureLoopBoundary(beatCount, Gp3To5Importer._maxBeatCount, 'beat count'); for (let i: number = 0; i < beatCount; i++) { this.readBeat(track, bar, newVoice); } @@ -732,7 +866,11 @@ export class Gp3To5Importer extends ScoreImporter { const beatTextAsLyrics = this.settings.importer.beatTextAsLyrics && track.index !== this._lyricsTrack; // detect if not lyrics track if ((flags & 0x04) !== 0) { - const text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + const text = GpBinaryHelpers.gpReadStringIntUnused( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); if (beatTextAsLyrics) { const lyrics = new Lyrics(); lyrics.text = text.trim(); @@ -763,9 +901,7 @@ export class Gp3To5Importer extends ScoreImporter { const note = this.readNote(track, bar, voice, newBeat, 6 - i); if (allNoteHarmonicType !== HarmonicType.None) { note.harmonicType = allNoteHarmonicType; - if (note.harmonicType === HarmonicType.Natural) { - note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(note.fret); - } + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(note.fret); } } } @@ -925,7 +1061,11 @@ export class Gp3To5Importer extends ScoreImporter { } } else { const strings: number = this._versionNumber >= 406 ? 7 : 6; - chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + chord.name = GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); chord.firstFret = IOHelper.readInt32LE(this.data); if (chord.firstFret > 0) { for (let i: number = 0; i < strings; i++) { @@ -1039,6 +1179,7 @@ export class Gp3To5Importer extends ScoreImporter { IOHelper.readInt32LE(this.data); // value const pointCount: number = IOHelper.readInt32LE(this.data); + this._ensureLoopBoundary(pointCount, Gp3To5Importer._maxBendPointCount, 'tremolo bar point count'); if (pointCount > 0) { for (let i: number = 0; i < pointCount; i++) { const point: BendPoint = new BendPoint(0, 0); @@ -1109,7 +1250,11 @@ export class Gp3To5Importer extends ScoreImporter { const phaser: number = IOHelper.readSInt8(this.data); const tremolo: number = IOHelper.readSInt8(this.data); if (this._versionNumber >= 500) { - tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); } tableChange.tempo = IOHelper.readInt32LE(this.data); @@ -1155,8 +1300,16 @@ export class Gp3To5Importer extends ScoreImporter { } // unknown if (this._versionNumber >= 510) { - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); - GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); + GpBinaryHelpers.gpReadStringIntByte( + this.data, + this.settings.importer.encoding, + this.settings.importer.maxDecodingBufferSize + ); } if (tableChange.volume >= 0) { const volumeAutomation: Automation = new Automation(); @@ -1337,6 +1490,8 @@ export class Gp3To5Importer extends ScoreImporter { IOHelper.readInt32LE(this.data); // value const pointCount: number = IOHelper.readInt32LE(this.data); + + this._ensureLoopBoundary(pointCount, Gp3To5Importer._maxBendPointCount, 'note bend point count'); if (pointCount > 0) { for (let i: number = 0; i < pointCount; i++) { const point: BendPoint = new BendPoint(0, 0); @@ -1443,10 +1598,30 @@ export class Gp3To5Importer extends ScoreImporter { note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(note.fret); break; case 2: - /*let _harmonicTone: number = */ this.data.readByte(); - /*let _harmonicKey: number = */ this.data.readByte(); - /*let _harmonicOctaveOffset: number = */ this.data.readByte(); + // C (0), D (2), E (4), F (5), G (7),A (9),B (11) + const harmonicTone: number = this.data.readByte(); + // b (255/-1), none (0), # (1) + let harmonicKey: number = this.data.readByte(); + if (harmonicKey === 255) { + harmonicKey = -1; + } + // Loco (0), 8va (1), 15ma (2) + const harmonicOctaveOffset: number = this.data.readByte(); + + const harmonicPitch = harmonicTone + harmonicKey; // 0-11 pitch class + const playedPitch = (note.fret + note.stringTuning) % 12; // 0-11 pitch class + + let targetHarmonic = harmonicPitch + harmonicOctaveOffset * 12; + + // Adjust to ensure harmonic is higher than played note (single octave should be enough with 0-11 played pitch) + if (targetHarmonic < playedPitch) { + targetHarmonic += 12; + } + + const deltaFrets = targetHarmonic - playedPitch; note.harmonicType = HarmonicType.Artificial; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(deltaFrets); + break; case 3: note.harmonicType = HarmonicType.Tap; @@ -1454,35 +1629,43 @@ export class Gp3To5Importer extends ScoreImporter { break; case 4: note.harmonicType = HarmonicType.Pinch; - note.harmonicValue = 12; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; case 5: note.harmonicType = HarmonicType.Semi; - note.harmonicValue = 12; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; } } else if (this._versionNumber >= 400) { switch (type) { case 1: note.harmonicType = HarmonicType.Natural; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(note.fret); break; case 3: note.harmonicType = HarmonicType.Tap; + // GP4 help: The tapped harmonic is an artificial harmonic obtained by tapping quickly on the string 12 frets higher. + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; case 4: note.harmonicType = HarmonicType.Pinch; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; case 5: note.harmonicType = HarmonicType.Semi; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; - case 15: + case 15: // artificial + 5 note.harmonicType = HarmonicType.Artificial; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(5); break; - case 17: + case 17: // artificial + 7 note.harmonicType = HarmonicType.Artificial; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(7); break; - case 22: + case 22: // artificial + 12 note.harmonicType = HarmonicType.Artificial; + note.harmonicValue = ModelUtils.deltaFretToHarmonicValue(12); break; } } @@ -1529,28 +1712,36 @@ export class GpBinaryHelpers { * Skips an integer (4byte) and reads a string using * a bytesize */ - public static gpReadStringIntUnused(data: IReadable, encoding: string): string { + public static gpReadStringIntUnused(data: IReadable, encoding: string, maxDecodingBufferSize: number): string { data.skip(4); - return GpBinaryHelpers.gpReadString(data, data.readByte(), encoding); + return GpBinaryHelpers.gpReadString(data, data.readByte(), encoding, maxDecodingBufferSize); } /** * Reads an integer as size, and then the string itself */ - public static gpReadStringInt(data: IReadable, encoding: string): string { - return GpBinaryHelpers.gpReadString(data, IOHelper.readInt32LE(data), encoding); + public static gpReadStringInt(data: IReadable, encoding: string, maxDecodingBufferSize: number): string { + return GpBinaryHelpers.gpReadString(data, IOHelper.readInt32LE(data), encoding, maxDecodingBufferSize); } /** * Reads an integer as size, skips a byte and reads the string itself */ - public static gpReadStringIntByte(data: IReadable, encoding: string): string { + public static gpReadStringIntByte(data: IReadable, encoding: string, maxDecodingBufferSize: number): string { const length: number = IOHelper.readInt32LE(data) - 1; data.readByte(); - return GpBinaryHelpers.gpReadString(data, length, encoding); + return GpBinaryHelpers.gpReadString(data, length, encoding, maxDecodingBufferSize); } - public static gpReadString(data: IReadable, length: number, encoding: string): string { + public static gpReadString( + data: IReadable, + length: number, + encoding: string, + maxDecodingBufferSize: number + ): string { + if (length > maxDecodingBufferSize) { + throw new OverflowError(`Detected string exceeding maxDecodingBufferSize at offset ${data.position}`); + } const b: Uint8Array = new Uint8Array(length); data.read(b, 0, b.length); return IOHelper.toString(b, encoding); @@ -1571,12 +1762,13 @@ export class GpBinaryHelpers { * @returns */ public static gpReadStringByteLength(data: IReadable, length: number, encoding: string): string { - const stringLength: number = data.readByte(); - const s: string = GpBinaryHelpers.gpReadString(data, stringLength, encoding); - if (stringLength < length) { - data.skip(length - stringLength); - } - return s; + // Fixed-width string field: 1 length byte + `length` data bytes, decoded + // up to min(stringLength, length). Always consumes 1 + length bytes. + const stringLength = data.readByte(); + const fieldBytes = new Uint8Array(length); + data.read(fieldBytes, 0, length); + const effectiveLength = Math.min(stringLength, length); + return IOHelper.toString(fieldBytes.subarray(0, effectiveLength), encoding); } } diff --git a/packages/alphatab/src/importer/Gp7To8Importer.ts b/packages/alphatab/src/importer/Gp7To8Importer.ts index 3503c85bc..8b9dbd96c 100644 --- a/packages/alphatab/src/importer/Gp7To8Importer.ts +++ b/packages/alphatab/src/importer/Gp7To8Importer.ts @@ -26,7 +26,7 @@ export class Gp7To8Importer extends ScoreImporter { // at first we need to load the binary file system // from the GPX container Logger.debug(this.name, 'Loading ZIP entries'); - const fileSystem: ZipReader = new ZipReader(this.data); + const fileSystem: ZipReader = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize); let entries: ZipEntry[]; try { entries = fileSystem.read(); @@ -78,7 +78,7 @@ export class Gp7To8Importer extends ScoreImporter { if (binaryStylesheetData) { Logger.debug(this.name, 'Start Parsing BinaryStylesheet'); - const stylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData); + const stylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData, this.settings.importer.maxDecodingBufferSize); stylesheet.apply(score); Logger.debug(this.name, 'BinaryStylesheet parsed'); } diff --git a/packages/alphatab/src/importer/GpifParser.ts b/packages/alphatab/src/importer/GpifParser.ts index b7bd46907..f5954de2c 100644 --- a/packages/alphatab/src/importer/GpifParser.ts +++ b/packages/alphatab/src/importer/GpifParser.ts @@ -2892,6 +2892,10 @@ export class GpifParser { // build masterbar automations for (const [barNumber, automations] of this._masterTrackAutomations) { + if (barNumber < 0 || barNumber >= this.score.masterBars.length) { + // automation references a bar that is not in the score's masterBars list + continue; + } const masterBar: MasterBar = this.score.masterBars[barNumber]; for (let i: number = 0, j: number = automations.length; i < j; i++) { const automation: Automation = automations[i]; diff --git a/packages/alphatab/src/importer/GpxImporter.ts b/packages/alphatab/src/importer/GpxImporter.ts index 722d7bcf9..7d180d7a5 100644 --- a/packages/alphatab/src/importer/GpxImporter.ts +++ b/packages/alphatab/src/importer/GpxImporter.ts @@ -69,7 +69,7 @@ export class GpxImporter extends ScoreImporter { if (binaryStylesheetData) { Logger.debug(this.name, 'Start Parsing BinaryStylesheet'); - const binaryStylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData); + const binaryStylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData, this.settings.importer.maxDecodingBufferSize); binaryStylesheet.apply(score); Logger.debug(this.name, 'BinaryStylesheet parsed'); } diff --git a/packages/alphatab/src/importer/MusicXmlImporter.ts b/packages/alphatab/src/importer/MusicXmlImporter.ts index 41ae4f533..4742ccce4 100644 --- a/packages/alphatab/src/importer/MusicXmlImporter.ts +++ b/packages/alphatab/src/importer/MusicXmlImporter.ts @@ -230,7 +230,7 @@ export class MusicXmlImporter extends ScoreImporter { } private _extractMusicXml(): string { - const zip = new ZipReader(this.data); + const zip = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize); let entries: ZipEntry[]; try { entries = zip.read(); @@ -1903,7 +1903,9 @@ export class MusicXmlImporter extends ScoreImporter { break; case 'percussion': bar.clef = Clef.Neutral; - bar.staff.isPercussion = true; + if(bar.index === 0){ + bar.staff.isPercussion = true; + } break; case 'tab': bar.clef = Clef.G2; @@ -2582,16 +2584,6 @@ export class MusicXmlImporter extends ScoreImporter { this._insertBeatToVoice(newBeat, voice); - if (note !== null) { - note!.isVisible = noteIsVisible; - const trackInfo = this._indexToTrackInfo.get(track.index)!; - if (instrumentId !== null) { - note!.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId!, note!); - } else if (!isPitched) { - note!.percussionArticulation = trackInfo.getOrCreateArticulation('', note!); - } - } - // duration only after we added it into the tree if (graceType !== GraceType.None) { newBeat.graceType = graceType; @@ -2759,6 +2751,52 @@ export class MusicXmlImporter extends ScoreImporter { // if not yet created do it befor we exit to ensure we created the beat/note ensureBeat(); + + if (note !== null) { + // Final note post-processing depends on the note already being attached to the + // beat/voice/bar/staff tree (e.g. percussion clef context on the resolved staff). + // Therefore this must run after ensureBeat() and after transposition has been applied. + this._finalizeImportedNote(note, track, instrumentId, isPitched, noteIsVisible); + } + } + + /** + * Applies note-level post-processing that requires the fully resolved parse context. + * + * Purpose: + * - Set final visibility. + * - Resolve percussion articulation consistently in one place. + * + * Why this is called at the end of _parseNote: + * - The logic relies on final note context (attached beat/voice/bar/staff), especially + * staff percussion state, and on the final display value after transposition. + * - Running this earlier could use incomplete or wrong context and produce wrong + * articulation mapping. + */ + private _finalizeImportedNote( + note: Note, + track: Track, + instrumentId: string | null, + isPitched: boolean, + noteIsVisible: boolean + ) { + note.isVisible = noteIsVisible; + + if (note.percussionArticulation >= 0) { + return; + } + + const trackInfo = this._indexToTrackInfo.get(track.index)!; + if (instrumentId !== null) { + note.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId, note); + } else if (note.beat.voice.bar.staff.isPercussion && isPitched) { + const knownArticulation = PercussionMapper.getArticulationById(note.displayValue); + if (knownArticulation) { + note.percussionArticulation = knownArticulation.id; + } + } else if (!isPitched) { + note.percussionArticulation = trackInfo.getOrCreateArticulation('', note); + } } private _parsePlay(element: XmlNode, note: Note | null) { diff --git a/packages/alphatab/src/importer/ScoreImporter.ts b/packages/alphatab/src/importer/ScoreImporter.ts index 5b0edc554..e914c29c9 100644 --- a/packages/alphatab/src/importer/ScoreImporter.ts +++ b/packages/alphatab/src/importer/ScoreImporter.ts @@ -1,4 +1,4 @@ -import type { IReadable } from '@coderline/alphatab/io/IReadable'; +import { ThrowingReadable, type IReadable } from '@coderline/alphatab/io/IReadable'; import { Score } from '@coderline/alphatab/model/Score'; import type { Settings } from '@coderline/alphatab/Settings'; @@ -15,7 +15,11 @@ export abstract class ScoreImporter { * Initializes the importer with the given data and settings. */ public init(data: IReadable, settings: Settings): void { - this.data = data; + if (data instanceof ThrowingReadable) { + this.data = data; + } else { + this.data = new ThrowingReadable(data); + } this.settings = settings; // when beginning reading a new score we reset the IDs. Score.resetIds(); diff --git a/packages/alphatab/src/importer/ScoreLoader.ts b/packages/alphatab/src/importer/ScoreLoader.ts index 2408aa243..c85c43005 100644 --- a/packages/alphatab/src/importer/ScoreLoader.ts +++ b/packages/alphatab/src/importer/ScoreLoader.ts @@ -4,6 +4,7 @@ import { AlphaTexImporter } from '@coderline/alphatab/importer/AlphaTexImporter' import type { ScoreImporter } from '@coderline/alphatab/importer/ScoreImporter'; import { UnsupportedFormatError } from '@coderline/alphatab/importer/UnsupportedFormatError'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; +import { ThrowingReadable } from '@coderline/alphatab/io/IReadable'; import { Logger } from '@coderline/alphatab/Logger'; import type { Score } from '@coderline/alphatab/model/Score'; import { Settings } from '@coderline/alphatab/Settings'; @@ -88,12 +89,12 @@ export class ScoreLoader { const importers: ScoreImporter[] = Environment.buildImporters(); Logger.debug('ScoreLoader', `Loading score from ${data.length} bytes using ${importers.length} importers`); let score: Score | null = null; - const bb: ByteBuffer = ByteBuffer.fromBuffer(data); + const readable = new ThrowingReadable(ByteBuffer.fromBuffer(data)); for (const importer of importers) { - bb.reset(); + readable.reset(); try { Logger.debug('ScoreLoader', `Importing using importer ${importer.name}`); - importer.init(bb, settings); + importer.init(readable, settings); score = importer.readScore(); Logger.debug('ScoreLoader', `Score imported using ${importer.name}`); break; diff --git a/packages/alphatab/src/io/IOHelper.ts b/packages/alphatab/src/io/IOHelper.ts index db8151068..2d3814ed8 100644 --- a/packages/alphatab/src/io/IOHelper.ts +++ b/packages/alphatab/src/io/IOHelper.ts @@ -155,7 +155,7 @@ export class IOHelper { encoding = 'utf-8'; } const decoder: TextDecoder = new TextDecoder(encoding); - return decoder.decode(data.buffer as ArrayBuffer); + return decoder.decode(data); } private static _detectEncoding(data: Uint8Array): string | null { diff --git a/packages/alphatab/src/io/IReadable.ts b/packages/alphatab/src/io/IReadable.ts index f76a10429..bbcde26a6 100644 --- a/packages/alphatab/src/io/IReadable.ts +++ b/packages/alphatab/src/io/IReadable.ts @@ -49,10 +49,73 @@ export interface IReadable { } /** - * @internal + * Thrown whenever we hit the end of input data unexpectedly. + * @public */ export class EndOfReaderError extends AlphaTabError { public constructor() { super(AlphaTabErrorType.Format, 'Unexpected end of data within reader'); } } + +/** + * Thrown whenever an overflow in data or buffer sizes is detected. + * @public + */ +export class OverflowError extends AlphaTabError { + public constructor(message: string) { + super(AlphaTabErrorType.Format, message); + } +} + +/** + * An {@see IReadable} implementation throwing when the end of stream is reached guarding against + * corrupted or maliciously crafted files leading to endless reading + * @internal + */ +export class ThrowingReadable implements IReadable { + private _readable: IReadable; + public constructor(readable: IReadable) { + this._readable = readable; + } + public get position(): number { + return this._readable.position; + } + + public set position(value: number) { + this._readable.position = value; + } + + public get length(): number { + return this._readable.length; + } + + public reset(): void { + this._readable.reset(); + } + + public skip(offset: number): void { + this._readable.skip(offset); + } + + private _requireBytes(bytes: number) { + const remaining = this.length - this.position; + if (remaining < bytes) { + throw new EndOfReaderError(); + } + } + + public readByte(): number { + this._requireBytes(1); + return this._readable.readByte(); + } + + public read(buffer: Uint8Array, offset: number, count: number): number { + this._requireBytes(count); + return this._readable.read(buffer, offset, count); + } + + public readAll(): Uint8Array { + return this._readable.readAll(); + } +} diff --git a/packages/alphatab/src/io/_barrel.ts b/packages/alphatab/src/io/_barrel.ts index 8491fe7c3..0f1d10278 100644 --- a/packages/alphatab/src/io/_barrel.ts +++ b/packages/alphatab/src/io/_barrel.ts @@ -1,4 +1,4 @@ export type { IWriteable } from '@coderline/alphatab/io/IWriteable'; -export type { IReadable } from '@coderline/alphatab/io/IReadable'; +export type { IReadable, OverflowError, EndOfReaderError } from '@coderline/alphatab/io/IReadable'; export { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; export { IOHelper } from '@coderline/alphatab/io/IOHelper'; diff --git a/packages/alphatab/src/midi/BeatTickLookup.ts b/packages/alphatab/src/midi/BeatTickLookup.ts index a33983bb6..4fed750c6 100644 --- a/packages/alphatab/src/midi/BeatTickLookup.ts +++ b/packages/alphatab/src/midi/BeatTickLookup.ts @@ -83,9 +83,6 @@ export class BeatTickLookup { * @param beat The beat to add. */ public highlightBeat(beat: Beat, playbackStart: number): void { - if (beat.isEmpty && !beat.voice.isEmpty) { - return; - } if (!this._highlightedBeats.has(beat.id)) { this._highlightedBeats.set(beat.id, true); this.highlightedBeats.push(new BeatTickLookupItem(beat, playbackStart)); diff --git a/packages/alphatab/src/midi/MasterBarTickLookup.ts b/packages/alphatab/src/midi/MasterBarTickLookup.ts index 4c9ed688c..3c19f3117 100644 --- a/packages/alphatab/src/midi/MasterBarTickLookup.ts +++ b/packages/alphatab/src/midi/MasterBarTickLookup.ts @@ -248,7 +248,6 @@ export class MasterBarTickLookup { if (this.firstBeat == null) { const n1 = new BeatTickLookup(sliceStart, end); n1.highlightBeat(beat, beatPlaybackStart); - this._insertAfter(this.firstBeat, n1); } // Variant B diff --git a/packages/alphatab/src/midi/MidiFileGenerator.ts b/packages/alphatab/src/midi/MidiFileGenerator.ts index 3c1729b82..dacb3ac59 100644 --- a/packages/alphatab/src/midi/MidiFileGenerator.ts +++ b/packages/alphatab/src/midi/MidiFileGenerator.ts @@ -726,7 +726,13 @@ export class MidiFileGenerator { let audioDuration: number = beat.playbackDuration; const masterBarDuration = beat.voice.bar.masterBar.calculateDuration(); - if (beat.voice.bar.isEmpty) { + // For a bar whose voice contains a single empty beat (the typical "whole-bar rest" + // placeholder inserted during score.finish), extend the beat's audio duration to cover + // the full bar so cursor navigation has a beat to follow across the whole bar. Don't + // apply this when the voice has multiple beats: those represent explicit rhythmic + // subdivisions even when each beat is empty (e.g. a recording grid of placeholder + // slots), and overriding would make every beat overlap the whole bar. + if (beat.voice.bar.isEmpty && beat.voice.beats.length === 1) { audioDuration = masterBarDuration; } else if ( beat.voice.bar.masterBar.tripletFeel !== TripletFeel.NoTripletFeel && diff --git a/packages/alphatab/src/midi/MidiTickLookup.ts b/packages/alphatab/src/midi/MidiTickLookup.ts index c8d7ba688..b8797d1d3 100644 --- a/packages/alphatab/src/midi/MidiTickLookup.ts +++ b/packages/alphatab/src/midi/MidiTickLookup.ts @@ -4,7 +4,7 @@ import { MasterBarTickLookup } from '@coderline/alphatab/midi/MasterBarTickLooku import { MidiUtils } from '@coderline/alphatab/midi/MidiUtils'; import type { Beat } from '@coderline/alphatab/model/Beat'; import type { MasterBar } from '@coderline/alphatab/model/MasterBar'; -import type { PlaybackRange } from '@coderline/alphatab/synth/PlaybackRange'; +import { PlaybackRange } from '@coderline/alphatab/synth/PlaybackRange'; /** * Describes how a cursor should be moving. @@ -188,6 +188,14 @@ export class MidiTickLookup { */ public readonly masterBarLookup: Map = new Map(); + /** + * A dictionary of all beat played. The index is the id to {@link Beat.id}. + * The value is the bar relative tick time at which the beat was registered during midi generation. + * This lookup only contains the first time a Beat is played. + * @internal + */ + public readonly beatLookup: Map = new Map(); + /** * A list of all {@link MasterBarTickLookup} sorted by time. */ @@ -671,11 +679,23 @@ export class MidiTickLookup { * @returns The time in midi ticks at which the beat is played the first time or 0 if the beat is not contained */ public getBeatStart(beat: Beat): number { - if (!this.masterBarLookup.has(beat.voice.bar.index)) { + if (!this.masterBarLookup.has(beat.voice.bar.index) || !this.beatLookup.has(beat.id)) { return 0; } - return this.masterBarLookup.get(beat.voice.bar.index)!.start + beat.playbackStart; + const mb = this.masterBarLookup.get(beat.voice.bar.index)!; + return mb.start + this.beatLookup.get(beat.id)!.startTick; + } + /** + * Gets the playback range in midi ticks for a given beat. + * @param beat The beat to find the time period for. + * @returns The relative playback range within the parent masterbar at which the beat start and ends playing + */ + public getRelativeBeatPlaybackRange(beat: Beat): PlaybackRange | undefined{ + if (!this.beatLookup.has(beat.id)) { + return undefined; + } + return this.beatLookup.get(beat.id)!; } /** @@ -695,6 +715,12 @@ export class MidiTickLookup { } public addBeat(beat: Beat, start: number, duration: number): void { + if (!this.beatLookup.has(beat.id)) { + const playbackRange = new PlaybackRange(); + playbackRange.startTick = start; + playbackRange.endTick = start + duration; + this.beatLookup.set(beat.id, playbackRange); + } const currentMasterBar = this._currentMasterBar; if (currentMasterBar) { // pre-beat grace notes at the start of the bar we also add the beat to the previous bar diff --git a/packages/alphatab/src/model/Beat.ts b/packages/alphatab/src/model/Beat.ts index 42fdc428f..a0cbf8df0 100644 --- a/packages/alphatab/src/model/Beat.ts +++ b/packages/alphatab/src/model/Beat.ts @@ -13,6 +13,7 @@ import { GraceType } from '@coderline/alphatab/model/GraceType'; import { Note } from '@coderline/alphatab/model/Note'; import { Ottavia } from '@coderline/alphatab/model/Ottavia'; import { PickStroke } from '@coderline/alphatab/model/PickStroke'; +import type { Slur } from '@coderline/alphatab/model/Slur'; import { TupletGroup } from '@coderline/alphatab/model/TupletGroup'; import { VibratoType } from '@coderline/alphatab/model/VibratoType'; import type { Voice } from '@coderline/alphatab/model/Voice'; @@ -687,6 +688,24 @@ export class Beat { */ public effectSlurDestination: Beat | null = null; + /** + * Convenience accessor for the {@link Slur} of this beat. Returns + * the effect slur of whichever note in this beat owns it (the + * chain-origin note populated during `Note.finish()`), or `null` + * when no note in the beat is an effect-slur origin. + * @clone_ignore + * @json_ignore + * @internal + */ + public get effectSlur(): Slur | null { + for (const n of this.notes) { + if (n.effectSlur !== null) { + return n.effectSlur; + } + } + return null; + } + /** * Gets or sets how the beaming should be done for this beat. */ diff --git a/packages/alphatab/src/model/Note.ts b/packages/alphatab/src/model/Note.ts index 1a55a49a6..df63ee78e 100644 --- a/packages/alphatab/src/model/Note.ts +++ b/packages/alphatab/src/model/Note.ts @@ -11,6 +11,8 @@ import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode import { Ottavia } from '@coderline/alphatab/model/Ottavia'; import { SlideInType } from '@coderline/alphatab/model/SlideInType'; import { SlideOutType } from '@coderline/alphatab/model/SlideOutType'; +import { Slur } from '@coderline/alphatab/model/Slur'; +import { SlurSegmentKind } from '@coderline/alphatab/model/SlurSegmentKind'; import type { Staff } from '@coderline/alphatab/model/Staff'; import { VibratoType } from '@coderline/alphatab/model/VibratoType'; import { NotationMode } from '@coderline/alphatab/NotationSettings'; @@ -604,6 +606,17 @@ export class Note { */ public effectSlurDestination: Note | null = null; + /** + * The {@link Slur} object whose origin is this note. Populated by + * `finish()`; non-null only on the chain-origin note of an effect + * slur. Carries the inner articulation segments used by the + * renderer to paint H/P/sl. labels along the arc. + * @clone_ignore + * @json_ignore + * @internal + */ + public effectSlur: Slur | null = null; + /** * The ornament applied on the note. */ @@ -906,21 +919,50 @@ export class Note { break; } let effectSlurDestination: Note | null = null; + let effectSlurSegmentKind: SlurSegmentKind | null = null; if (this.isHammerPullOrigin && this.hammerPullDestination) { effectSlurDestination = this.hammerPullDestination; + effectSlurSegmentKind = SlurSegmentKind.HammerPull; } else if (this.slideOutType === SlideOutType.Legato && this.slideTarget) { effectSlurDestination = this.slideTarget; + effectSlurSegmentKind = SlurSegmentKind.LegatoSlide; } if (effectSlurDestination) { this.hasEffectSlur = true; if (this.effectSlurOrigin && this.beat.pickStroke === PickStroke.None) { - this.effectSlurOrigin.effectSlurDestination = effectSlurDestination; - this.effectSlurOrigin.effectSlurDestination.effectSlurOrigin = this.effectSlurOrigin; + const chainOrigin = this.effectSlurOrigin; + chainOrigin.effectSlurDestination = effectSlurDestination; + effectSlurDestination.effectSlurOrigin = chainOrigin; this.effectSlurOrigin = null; + + if (effectSlurSegmentKind !== null && chainOrigin.effectSlur !== null) { + chainOrigin.effectSlur.destinationNote = effectSlurDestination; + chainOrigin.effectSlur.segments.push({ + fromNote: this, + toNote: effectSlurDestination, + kind: effectSlurSegmentKind, + text: null + }); + } } else { this.isEffectSlurOrigin = true; this.effectSlurDestination = effectSlurDestination; - this.effectSlurDestination.effectSlurOrigin = this; + effectSlurDestination.effectSlurOrigin = this; + + // Always allocate a fresh Slur — finish() may run twice (worker re-finish); + // overwriting unconditionally keeps the derivation idempotent. + const slur = new Slur(); + slur.originNote = this; + slur.destinationNote = effectSlurDestination; + if (effectSlurSegmentKind !== null) { + slur.segments.push({ + fromNote: this, + toNote: effectSlurDestination, + kind: effectSlurSegmentKind, + text: null + }); + } + this.effectSlur = slur; } } // try to detect what kind of bend was used and cleans unneeded points if required diff --git a/packages/alphatab/src/model/Slur.ts b/packages/alphatab/src/model/Slur.ts new file mode 100644 index 000000000..8a42f15be --- /dev/null +++ b/packages/alphatab/src/model/Slur.ts @@ -0,0 +1,19 @@ +import type { Note } from '@coderline/alphatab/model/Note'; +import type { SlurSegment } from '@coderline/alphatab/model/SlurSegment'; + +/** + * A slur arc spanning two notes, optionally with inner articulation + * segments. Corresponds conceptually to a MusicXML `` element + * plus the technique spans inside it. + * + * For this PR only effect slurs (hammer-pull + legato-slide chains) + * are derived in `Note.finish()`. Phrase and legato slurs may join + * this type in a future PR; a discriminator will be added at that + * point. + * @internal + */ +export class Slur { + public originNote!: Note; + public destinationNote!: Note; + public segments: SlurSegment[] = []; +} diff --git a/packages/alphatab/src/model/SlurSegment.ts b/packages/alphatab/src/model/SlurSegment.ts new file mode 100644 index 000000000..453908df3 --- /dev/null +++ b/packages/alphatab/src/model/SlurSegment.ts @@ -0,0 +1,22 @@ +import type { Note } from '@coderline/alphatab/model/Note'; +import type { SlurSegmentKind } from '@coderline/alphatab/model/SlurSegmentKind'; + +/** + * One inner articulation span inside a {@link Slur}. Corresponds + * conceptually to a MusicXML `` / `` / `` + * start-stop pair nested inside the surrounding `` element. + * @record + * @internal + */ +export interface SlurSegment { + fromNote: Note; + toNote: Note; + kind: SlurSegmentKind; + /** + * Optional explicit label preserved from an external source (e.g. a + * future importer that reads MusicXML element text content). + * When null, the renderer derives the label from `kind` and note + * context — H vs P by fret/realValue comparison, "sl." for slides. + */ + text: string | null; +} diff --git a/packages/alphatab/src/model/SlurSegmentKind.ts b/packages/alphatab/src/model/SlurSegmentKind.ts new file mode 100644 index 000000000..ce58149a1 --- /dev/null +++ b/packages/alphatab/src/model/SlurSegmentKind.ts @@ -0,0 +1,13 @@ +/** + * Articulation kind for an inner span of a {@link Slur}. + * + * Drives the renderer's font selection (which {@link NotationElement} to + * use) and the default label text when {@link SlurSegment.text} is null. + * `Note.finish()` classifies the kind once when building the slur; the + * renderer never re-derives it. + * @internal + */ +export enum SlurSegmentKind { + HammerPull = 0, + LegatoSlide = 1 +} diff --git a/packages/alphatab/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts b/packages/alphatab/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts index a0db21058..204aef546 100644 --- a/packages/alphatab/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts +++ b/packages/alphatab/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts @@ -39,7 +39,7 @@ declare let AudioWorkletProcessor: { * @internal */ interface AudioWorkletNode extends AudioNode { - readonly port: AudioWorkletProcessorMessagePort; + readonly port: AudioWorkletProcessorMessagePort; } // Bug 646: Safari 14.1 is buggy regarding audio worklets diff --git a/packages/alphatab/src/platform/javascript/BrowserUiFacade.ts b/packages/alphatab/src/platform/javascript/BrowserUiFacade.ts index c3205d2b4..4f2c7d147 100644 --- a/packages/alphatab/src/platform/javascript/BrowserUiFacade.ts +++ b/packages/alphatab/src/platform/javascript/BrowserUiFacade.ts @@ -36,7 +36,7 @@ import { BackingTrackPlayer } from '@coderline/alphatab/synth/BackingTrackPlayer import { CoreSettings, FontFileFormat } from '@coderline/alphatab/CoreSettings'; import type { IAudioExporterWorker } from '@coderline/alphatab/synth/IAudioExporter'; import { AlphaSynthAudioExporterWorkerApi } from '@coderline/alphatab/platform/worker/AlphaSynthAudioExporterWorkerApi'; -import { IAlphaTabRenderingWorker, IAlphaSynthWorker } from '@coderline/alphatab/platform/worker/AlphaTabWorkerProtocol'; +import type { IAlphaTabRenderingWorker, IAlphaSynthWorker } from '@coderline/alphatab/platform/worker/AlphaTabWorkerProtocol'; import { ScoreRenderer } from '@coderline/alphatab/rendering/ScoreRenderer'; /** diff --git a/packages/alphatab/src/platform/worker/AlphaTabWebWorker.ts b/packages/alphatab/src/platform/worker/AlphaTabWebWorker.ts index 191cdbceb..f5cf1f212 100644 --- a/packages/alphatab/src/platform/worker/AlphaTabWebWorker.ts +++ b/packages/alphatab/src/platform/worker/AlphaTabWebWorker.ts @@ -85,9 +85,10 @@ export class AlphaTabWebWorker { break; case 'alphaTab.renderScore': this._updateFontSizes(data.fontSizes); + const renderHints = data.renderHints; const score = data.score == null ? null : JsonConverter.jsObjectToScore(data.score, this._renderer.settings); - this._renderMultiple(score, data.trackIndexes); + this._renderMultiple(score, data.trackIndexes, renderHints); break; case 'alphaTab.updateSettings': this._updateSettings(data.settings); diff --git a/packages/alphatab/src/rendering/BarRendererBase.ts b/packages/alphatab/src/rendering/BarRendererBase.ts index c3de38ea2..dd015bec6 100644 --- a/packages/alphatab/src/rendering/BarRendererBase.ts +++ b/packages/alphatab/src/rendering/BarRendererBase.ts @@ -214,6 +214,16 @@ export class BarRendererBase { return false; } + /** + * The fixed-overhead width of this renderer: glyphs that do not stretch when + * the bar is scaled (clef, key signature, time signature, barlines, courtesy + * accidentals, etc). Treated as a fixed allocation by the system-level layout + * before distributing remaining width across bars by {@link Bar.displayScale}. + */ + public get fixedOverhead(): number { + return this._preBeatGlyphs.width + this._postBeatGlyphs.width; + } + public scaleToWidth(width: number): void { // preBeat and postBeat glyphs do not get resized const containerWidth: number = width - this._preBeatGlyphs.width - this._postBeatGlyphs.width; diff --git a/packages/alphatab/src/rendering/IScoreRenderer.ts b/packages/alphatab/src/rendering/IScoreRenderer.ts index 0a071a9c7..da4daa400 100644 --- a/packages/alphatab/src/rendering/IScoreRenderer.ts +++ b/packages/alphatab/src/rendering/IScoreRenderer.ts @@ -19,6 +19,14 @@ export interface RenderHints { * internally it might still be decided to clear the viewport. */ reuseViewport?: boolean; + + /** + * Indicates the index of the first masterbar which was modified in the data model. + * @remarks + * AlphaTab will try to optimize the rendering and other updates to keep unchanged parts. + * At this point only the rendering is affected and the generated MIDI has to be updated separately. + */ + firstChangedMasterBar?: number; } /** diff --git a/packages/alphatab/src/rendering/LineBarRenderer.ts b/packages/alphatab/src/rendering/LineBarRenderer.ts index bfee20a8e..2cf28ab75 100644 --- a/packages/alphatab/src/rendering/LineBarRenderer.ts +++ b/packages/alphatab/src/rendering/LineBarRenderer.ts @@ -348,11 +348,27 @@ export abstract class LineBarRenderer extends BarRendererBase { const firstNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(firstNonRestBeat)!; const lastNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(lastNonRestBeat)!; const direction = this.getTupletBeamDirection(firstNonRestBeamingHelper); - let startY: number = this.calculateBeamYWithDirection(firstNonRestBeamingHelper, startX, direction); - let endY: number = this.calculateBeamYWithDirection(lastNonRestBeamingHelper, endX, direction); + let startY: number; + let endY: number; if (isRestOnly) { - startY = Math.max(startY, endY); + // rests have no stems, so anchor to the actual rest glyph bounds + // instead of a stem-adjusted flag position (which would place the bracket + // a full quarter-stem length away from the rests). + if (direction === BeamDirection.Up) { + startY = Math.min( + this.getRestY(firstNonRestBeat, NoteYPosition.Top), + this.getRestY(lastNonRestBeat, NoteYPosition.Top) + ); + } else { + startY = Math.max( + this.getRestY(firstNonRestBeat, NoteYPosition.Bottom), + this.getRestY(lastNonRestBeat, NoteYPosition.Bottom) + ); + } endY = startY; + } else { + startY = this.calculateBeamYWithDirection(firstNonRestBeamingHelper, startX, direction); + endY = this.calculateBeamYWithDirection(lastNonRestBeamingHelper, endX, direction); } // align line centered in available space @@ -874,7 +890,33 @@ export abstract class LineBarRenderer extends BarRendererBase { for (const v of this.helpers.beamHelpers) { for (const h of v) { if (!this.shouldPaintBeamingHelper(h)) { - // no visible helper + // beam is not drawn, but a rest-only tuplet still draws a bracket + // anchored to the rest glyph bounds and needs overflow reserved. + if (h.hasTuplet && h.isRestBeamHelper) { + const tupletGroup = h.beats[0].tupletGroup!; + const tupletFirst = tupletGroup.beats[0]; + const tupletLast = tupletGroup.beats[tupletGroup.beats.length - 1]; + const tupletDirection = this.getTupletBeamDirection(h); + if (tupletDirection === BeamDirection.Up) { + const restTop = Math.min( + this.getRestY(tupletFirst, NoteYPosition.Top), + this.getRestY(tupletLast, NoteYPosition.Top) + ); + const topY = restTop - this.tupletSize - this.tupletOffset; + if (topY < maxNoteY) { + maxNoteY = topY; + } + } else { + const restBottom = Math.max( + this.getRestY(tupletFirst, NoteYPosition.Bottom), + this.getRestY(tupletLast, NoteYPosition.Bottom) + ); + const bottomY = restBottom + this.tupletSize + this.tupletOffset; + if (bottomY > minNoteY) { + minNoteY = bottomY; + } + } + } } // notes with stems (and potential flags) else if (h.beats.length === 1 && h.beats[0].duration >= Duration.Half) { diff --git a/packages/alphatab/src/rendering/ScoreRenderer.ts b/packages/alphatab/src/rendering/ScoreRenderer.ts index c534c4261..5c33e3e11 100644 --- a/packages/alphatab/src/rendering/ScoreRenderer.ts +++ b/packages/alphatab/src/rendering/ScoreRenderer.ts @@ -140,7 +140,14 @@ export class ScoreRenderer implements IScoreRenderer { Logger.warning('Rendering', 'AlphaTab skipped rendering because of width=0 (element invisible)', null); return; } - this.boundsLookup = new BoundsLookup(); + // For partial renders we preserve the existing lookup so bars outside the re-layouted + // range keep their already-scaled bounds - the layout will clear the changed range + // before the paint pass re-registers fresh entries for it. + if (renderHints?.firstChangedMasterBar !== undefined && this.boundsLookup) { + this.boundsLookup.resetForPartialUpdate(); + } else { + this.boundsLookup = new BoundsLookup(); + } this._recreateCanvas(); this.canvas!.lineWidth = 1; this.canvas!.settings = this.settings; diff --git a/packages/alphatab/src/rendering/glyphs/ScoreSlurGlyph.ts b/packages/alphatab/src/rendering/glyphs/ScoreSlurGlyph.ts index 8735bd156..4e09d2e11 100644 --- a/packages/alphatab/src/rendering/glyphs/ScoreSlurGlyph.ts +++ b/packages/alphatab/src/rendering/glyphs/ScoreSlurGlyph.ts @@ -2,16 +2,36 @@ import { GraceType } from '@coderline/alphatab/model/GraceType'; import { NoteXPosition, NoteYPosition } from '@coderline/alphatab/rendering/BarRendererBase'; import { BeatXPosition } from '@coderline/alphatab/rendering/BeatXPosition'; import { ScoreTieGlyph } from '@coderline/alphatab/rendering/glyphs/ScoreTieGlyph'; +import { TieGlyphLabels, type TieGlyphLabel } from '@coderline/alphatab/rendering/glyphs/TieGlyphLabel'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; /** * @internal */ export class ScoreSlurGlyph extends ScoreTieGlyph { + private _labels: TieGlyphLabel[] | null = null; + public override getTieHeight(startX: number, _startY: number, endX: number, _endY: number): number { return (Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2; } + protected override getSlurLabels(): TieGlyphLabel[] | null { + if (this._labels === null) { + this._labels = []; + const slur = this.startNote.beat.effectSlur; + if (slur !== null) { + const notationSettings = this.renderer.settings.notation; + for (const s of slur.segments) { + const label = TieGlyphLabels.build(s, s.toNote.realValue >= s.fromNote.realValue); + if (notationSettings.isNotationElementVisible(label.element)) { + this._labels.push(label); + } + } + } + } + return this._labels.length > 0 ? this._labels : null; + } + protected override calculateStartX(): number { return ( this.renderer.x + diff --git a/packages/alphatab/src/rendering/glyphs/TabSlurGlyph.ts b/packages/alphatab/src/rendering/glyphs/TabSlurGlyph.ts index 97ba66030..380eeaf13 100644 --- a/packages/alphatab/src/rendering/glyphs/TabSlurGlyph.ts +++ b/packages/alphatab/src/rendering/glyphs/TabSlurGlyph.ts @@ -1,5 +1,6 @@ import type { Note } from '@coderline/alphatab/model/Note'; import { TabTieGlyph } from '@coderline/alphatab/rendering/glyphs/TabTieGlyph'; +import { TieGlyphLabels, type TieGlyphLabel } from '@coderline/alphatab/rendering/glyphs/TieGlyphLabel'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; /** @@ -7,8 +8,9 @@ import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection */ export class TabSlurGlyph extends TabTieGlyph { private _forSlide: boolean; + private _labels: TieGlyphLabel[] | null = null; - public constructor(slurEffectId: string, startNote: Note, endNote: Note, forSlide: boolean, forEnd:boolean) { + public constructor(slurEffectId: string, startNote: Note, endNote: Note, forSlide: boolean, forEnd: boolean) { super(slurEffectId, startNote, endNote, forEnd); this._forSlide = forSlide; } @@ -17,6 +19,23 @@ export class TabSlurGlyph extends TabTieGlyph { return (Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2; } + protected override getSlurLabels(): TieGlyphLabel[] | null { + if (this._labels === null) { + this._labels = []; + const slur = this.startNote.effectSlur; + if (slur !== null) { + const notationSettings = this.renderer.settings.notation; + for (const s of slur.segments) { + const label = TieGlyphLabels.build(s, s.toNote.fret >= s.fromNote.fret); + if (notationSettings.isNotationElementVisible(label.element)) { + this._labels.push(label); + } + } + } + } + return this._labels.length > 0 ? this._labels : null; + } + public tryExpand(startNote: Note, endNote: Note, forSlide: boolean, forEnd: boolean): boolean { // same type required if (this._forSlide !== forSlide) { @@ -42,6 +61,7 @@ export class TabSlurGlyph extends TabTieGlyph { case BeamDirection.Up: if (startNote.realValue > this.startNote.realValue) { this.startNote = startNote; + this._labels = null; // invalidate cache — labels live on startNote } if (endNote.realValue > this.endNote.realValue) { this.endNote = endNote; @@ -50,6 +70,7 @@ export class TabSlurGlyph extends TabTieGlyph { case BeamDirection.Down: if (startNote.realValue < this.startNote.realValue) { this.startNote = startNote; + this._labels = null; } if (endNote.realValue < this.endNote.realValue) { this.endNote = endNote; diff --git a/packages/alphatab/src/rendering/glyphs/TieGlyph.ts b/packages/alphatab/src/rendering/glyphs/TieGlyph.ts index 5b96249a0..f19a036eb 100644 --- a/packages/alphatab/src/rendering/glyphs/TieGlyph.ts +++ b/packages/alphatab/src/rendering/glyphs/TieGlyph.ts @@ -1,7 +1,8 @@ import type { Note } from '@coderline/alphatab/model/Note'; -import type { ICanvas } from '@coderline/alphatab/platform/ICanvas'; +import { TextAlign, TextBaseline, type ICanvas } from '@coderline/alphatab/platform/ICanvas'; import { type BarRendererBase, NoteXPosition, NoteYPosition } from '@coderline/alphatab/rendering/BarRendererBase'; import { Glyph } from '@coderline/alphatab/rendering/glyphs/Glyph'; +import type { ResolvedTieGlyphLabel, TieGlyphLabel } from '@coderline/alphatab/rendering/glyphs/TieGlyphLabel'; import type { LineBarRenderer } from '@coderline/alphatab/rendering/LineBarRenderer'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; import { Bounds } from '@coderline/alphatab/rendering/utils/Bounds'; @@ -38,6 +39,12 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { private _tieHeight: number = 0; private _boundingBox?: Bounds; private _shouldPaint: boolean = false; + // Resolved per-label paint state. Lazily grown; re-layouts mutate + // existing entries in place and update `_resolvedLabelCount` to + // signal how many of them are valid this pass. + private _resolvedLabels: ResolvedTieGlyphLabel[] = []; + private _resolvedLabelCount: number = 0; + private _labelBaselineOffset: number = 0; public get checkForOverflow() { return this._shouldPaint && this._boundingBox !== undefined; @@ -119,7 +126,14 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { this._boundingBox = undefined; this.y = Math.min(this._startY, this._endY); + const down = this.tieDirection === BeamDirection.Down; let tieBoundingBox: Bounds; + // Bezier control points for the tie. Computed once and reused + // for both the bounding box (via _calculateActualTieHeightFromCps) + // and label-apex sampling further below — avoids a redundant + // call to _computeBezierControlPoints (and its 14-element array + // allocation) per labeled slur per layout. + let cps: number[] = []; if (this.shouldDrawBendSlur()) { this._tieHeight = 0; tieBoundingBox = TieGlyph.calculateBendSlurHeight( @@ -127,25 +141,100 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { this._startY, this._endX, this._endY, - this.tieDirection === BeamDirection.Down, + down, this.renderer.smuflMetrics.tieHeight ); } else { this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY); - - tieBoundingBox = TieGlyph.calculateActualTieHeight( + const tieThickness = this.renderer.smuflMetrics.tieMidpointThickness; + cps = TieGlyph._computeBezierControlPoints( 1, this._startX, this._startY, this._endX, this._endY, - this.tieDirection === BeamDirection.Down, + down, this._tieHeight, - this.renderer.smuflMetrics.tieMidpointThickness + tieThickness + ); + tieBoundingBox = TieGlyph._calculateActualTieHeightFromCps( + cps, + this._startX, + this._startY, + this._endX, + this._endY, + down, + tieThickness ); } this._boundingBox = tieBoundingBox; + this._resolvedLabelCount = 0; + const labels = this.getSlurLabels(); + if (labels !== null && labels.length > 0 && this.shouldPaintLabels()) { + const res = this.renderer.settings.display.resources; + const padding = this.renderer.smuflMetrics.oneStaffSpace * 0.25; + let maxTextHeight = 0; + + // Single Y line for all labels — the outer arc apex. + // Painted offset adds `padding` on the outward side, so + // every label sits the same fixed distance from its arc. + const labelLineY = cps.length > 0 + ? 0.125 * cps[7] + 0.375 * cps[9] + 0.375 * cps[11] + 0.125 * cps[13] + : (this._startY + this._endY) / 2; + + for (const label of labels) { + const fromX = this.resolveLabelAnchorX(label.fromNote); + const toX = this.resolveLabelAnchorX(label.toNote); + if (fromX === null || toX === null) { + continue; + } + const midX = (fromX + toX) / 2; + if (midX < this._startX || midX > this._endX) { + continue; + } + + // Per-element font.size as an upper bound on glyph + // height — avoids per-label measureText calls. All H/P + // and sl. labels use the same _effectFont, so this is + // typically computed once. + const font = res.getFontForNotationElement(label.element); + if (font.size > maxTextHeight) { + maxTextHeight = font.size; + } + + // grow cache lazily; mutate existing slot in place otherwise + let slot: ResolvedTieGlyphLabel; + if (this._resolvedLabelCount < this._resolvedLabels.length) { + slot = this._resolvedLabels[this._resolvedLabelCount]; + slot.x = midX; + slot.y = labelLineY; + slot.text = label.text; + slot.element = label.element; + } else { + slot = { + x: midX, + y: labelLineY, + text: label.text, + element: label.element + }; + this._resolvedLabels.push(slot); + } + this._resolvedLabelCount++; + } + + if (this._resolvedLabelCount > 0) { + // canvas.textBaseline is 'hanging' (TextBaseline.Top), so + // fillText positions `y` at the glyph's top edge. + if (this.tieDirection === BeamDirection.Up) { + tieBoundingBox.y -= maxTextHeight + padding; + this._labelBaselineOffset = -(maxTextHeight + padding); + } else { + this._labelBaselineOffset = padding; + } + tieBoundingBox.h += maxTextHeight + padding; + } + } this.height = tieBoundingBox.h; @@ -165,6 +254,8 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { return; } + const isDown = this.tieDirection === BeamDirection.Down; + if (this.shouldDrawBendSlur()) { TieGlyph.drawBendSlur( canvas, @@ -172,7 +263,7 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { cy + this._startY, cx + this._endX, cy + this._endY, - this.tieDirection === BeamDirection.Down, + isDown, this.renderer.smuflMetrics.tieHeight ); } else { @@ -183,11 +274,79 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { cy + this._startY, cx + this._endX, cy + this._endY, - this.tieDirection === BeamDirection.Down, + isDown, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness ); } + + if (this._resolvedLabelCount > 0) { + const ta = canvas.textAlign; + const tb = canvas.textBaseline; + canvas.textAlign = TextAlign.Center; + canvas.textBaseline = TextBaseline.Top; + const res = this.renderer.resources; + let lastElement = -1; + for (let i = 0; i < this._resolvedLabelCount; i++) { + const label = this._resolvedLabels[i]; + if (label.element !== lastElement) { + canvas.font = res.getFontForNotationElement(label.element); + lastElement = label.element; + } + canvas.fillText(label.text, cx + label.x, cy + label.y + this._labelBaselineOffset); + } + canvas.textAlign = ta; + canvas.textBaseline = tb; + } + } + + /** + * Returns the labels to paint along this slur, or `null` when there + * are none. Override in subclasses. + */ + protected getSlurLabels(): TieGlyphLabel[] | null { + return null; + } + + /** + * Whether label painting is enabled. Defaults to `true`. Subclasses + * may override to disable labels on the bend-slur path or other + * special cases. + */ + protected shouldPaintLabels(): boolean { + return !this.shouldDrawBendSlur(); + } + + /** + * Looks up the absolute X coordinate of an anchor note. Reuses + * the start/end bar renderers already resolved by the subclass + * (NoteTieGlyph) when the note's bar matches — most labels live + * in the slur's start or end bar, so this avoids the double Map + * lookup in `getRendererForBar` per label per layout. Returns + * `null` when the note's bar is not rendered on this glyph's + * staff (cross-system case). + */ + protected resolveLabelAnchorX(note: Note): number | null { + const bar = note.beat.voice.bar; + let renderer: LineBarRenderer | null = null; + const start = this.lookupStartBeatRenderer(); + if (start !== null && start.bar === bar) { + renderer = start; + } else { + const end = this.lookupEndBeatRenderer(); + if (end !== null && end.bar === bar) { + renderer = end; + } else { + renderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff!.staffId, + bar + ) as LineBarRenderer | null; + } + } + if (renderer === null) { + return null; + } + return renderer.x + renderer.getNoteX(note, NoteXPosition.Center); } protected abstract shouldDrawBendSlur(): boolean; @@ -236,12 +395,27 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { size: number ): Bounds { const cp = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size); + return TieGlyph._calculateActualTieHeightFromCps(cp, x1, y1, x2, y2, down, size); + } + + /** + * Derives the bounding box for a tie from already-computed control + * points. Splits the bbox math from cps generation so callers that + * need BOTH cps and bbox (e.g. multi-label slur layout) avoid a + * second call to `_computeBezierControlPoints`. + */ + private static _calculateActualTieHeightFromCps( + cp: number[], + x1: number, + y1: number, + x2: number, + y2: number, + down: boolean, + size: number + ): Bounds { if (cp.length === 0) { return new Bounds(x1, y1, x2 - x1, y2 - y1); } - - // For a musical tie/slur, the extrema occur predictably near the midpoint - // Evaluate at midpoint (t=0.5) and check endpoints const p0x = cp[0]; const p0y = cp[1]; const c1x = cp[2]; @@ -251,17 +425,14 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { const p1x = cp[6]; const p1y = cp[7]; - // Evaluate at t=0.5 for midpoint const midX = 0.125 * p0x + 0.375 * c1x + 0.375 * c2x + 0.125 * p1x; const midY = 0.125 * p0y + 0.375 * c1y + 0.375 * c2y + 0.125 * p1y; - // Bounds are simply min/max of start, end, and midpoint const xMin = Math.min(p0x, p1x, midX); const xMax = Math.max(p0x, p1x, midX); let yMin = Math.min(p0y, p1y, midY); let yMax = Math.max(p0y, p1y, midY); - // Account for thickness of the tie/slur if (down) { yMax += size; } else { @@ -360,6 +531,7 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph { return [rotateX + rx, rotateY + ry]; } + public static paintTie( canvas: ICanvas, scale: number, diff --git a/packages/alphatab/src/rendering/glyphs/TieGlyphLabel.ts b/packages/alphatab/src/rendering/glyphs/TieGlyphLabel.ts new file mode 100644 index 000000000..8e6ca4359 --- /dev/null +++ b/packages/alphatab/src/rendering/glyphs/TieGlyphLabel.ts @@ -0,0 +1,63 @@ +import type { Note } from '@coderline/alphatab/model/Note'; +import type { SlurSegment } from '@coderline/alphatab/model/SlurSegment'; +import { SlurSegmentKind } from '@coderline/alphatab/model/SlurSegmentKind'; +import { NotationElement } from '@coderline/alphatab/NotationSettings'; + +/** + * One resolved label to paint along a tie/slur arc. Built once per + * slur glyph from the model-side {@link SlurSegment}s; consumed by + * `TieGlyph` during layout (X/Y resolution) and paint. + * @record + * @internal + */ +export interface TieGlyphLabel { + fromNote: Note; + toNote: Note; + text: string; + element: NotationElement; +} + +/** + * A label whose paint position has been resolved against the current + * layout. Stored on the glyph in a lazily-grown cache so re-layouts + * mutate existing entries instead of allocating. + * @record + * @internal + */ +export interface ResolvedTieGlyphLabel { + x: number; + y: number; + text: string; + element: NotationElement; +} + +/** + * Helpers for building `TieGlyphLabel` instances from model-side + * {@link SlurSegment}s. + * @internal + */ +export class TieGlyphLabels { + /** + * Builds a `TieGlyphLabel` for one segment of a slur. The + * `isAscending` flag selects between the H/P glyph for hammer-on + * vs. pull-off — score side passes a comparison on `realValue`, + * tab side passes a comparison on `fret`. + */ + public static build(s: SlurSegment, isAscending: boolean): TieGlyphLabel { + if (s.kind === SlurSegmentKind.LegatoSlide) { + return { + fromNote: s.fromNote, + toNote: s.toNote, + text: s.text !== null ? s.text : 'sl.', + element: NotationElement.EffectSlideText + }; + } + // HammerPull + return { + fromNote: s.fromNote, + toNote: s.toNote, + text: s.text !== null ? s.text : isAscending ? 'H' : 'P', + element: NotationElement.EffectHammerOnPullOffText + }; + } +} diff --git a/packages/alphatab/src/rendering/layout/HorizontalScreenLayout.ts b/packages/alphatab/src/rendering/layout/HorizontalScreenLayout.ts index 5962fab06..f613fe331 100644 --- a/packages/alphatab/src/rendering/layout/HorizontalScreenLayout.ts +++ b/packages/alphatab/src/rendering/layout/HorizontalScreenLayout.ts @@ -44,6 +44,14 @@ export class HorizontalScreenLayout extends ScoreLayout { public doResize(): void { // not supported } + + public override doUpdateForBars(_renderHints: RenderHints): boolean { + // not supported yet, modifications likely cause anyhow full updates + // as we do not optimize effect bands yet. with effect bands being more + // isolated in bars we could try updating dynamically + return false; + } + protected doLayoutAndRender(renderHints: RenderHints | undefined): void { const score: Score = this.renderer.score!; @@ -61,6 +69,11 @@ export class HorizontalScreenLayout extends ScoreLayout { endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex)); this._system = this.createEmptyStaffSystem(0); + // Each bar in horizontal layout is sized independently (by bar.displayWidth or the bar's + // intrinsic width), so there is no shared staff width to distribute across bars. Keep each + // bar's spring constants referenced against its own local minimum-duration so rendering + // matches the historical per-bar behaviour. + this._system.shareMinDurationAcrossBars = false; this._system.isLast = true; this._system.x = this.pagePadding![0]; this._system.y = this.pagePadding![1]; @@ -150,7 +163,7 @@ export class HorizontalScreenLayout extends ScoreLayout { } this.height = this.layoutAndRenderBottomScoreInfo(this.height); - this.height = this.layoutAndRenderAnnotation(this.height); + this.height = this._layoutAndRenderAnnotation(this.height); this.height += this.pagePadding![3]; diff --git a/packages/alphatab/src/rendering/layout/ScoreLayout.ts b/packages/alphatab/src/rendering/layout/ScoreLayout.ts index 48b41ac17..cc6585301 100644 --- a/packages/alphatab/src/rendering/layout/ScoreLayout.ts +++ b/packages/alphatab/src/rendering/layout/ScoreLayout.ts @@ -29,14 +29,11 @@ import { Lazy } from '@coderline/alphatab/util/Lazy'; /** * @internal + * @record */ -class LazyPartial { - public args: RenderFinishedEventArgs; - public renderCallback: (canvas: ICanvas) => void; - public constructor(args: RenderFinishedEventArgs, renderCallback: (canvas: ICanvas) => void) { - this.args = args; - this.renderCallback = renderCallback; - } +interface LazyPartial { + args: RenderFinishedEventArgs; + renderCallback: (canvas: ICanvas) => void; } /** @@ -84,13 +81,10 @@ export abstract class ScoreLayout { } public abstract doResize(): void; + public abstract doUpdateForBars(renderHints: RenderHints): boolean; + public layoutAndRender(renderHints?: RenderHints): void { - this._lazyPartials.clear(); this.slurRegistry.clear(); - this.beamingRuleLookups.clear(); - this._barRendererLookup.clear(); - - this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile)!; const score: Score = this.renderer.score!; @@ -102,6 +96,19 @@ export abstract class ScoreLayout { this.lastBarIndex ); + const firstChangedMasterBar = renderHints?.firstChangedMasterBar; + if (firstChangedMasterBar !== undefined) { + if (this.doUpdateForBars(renderHints!)) { + return; + } + } + + this._lazyPartials.clear(); + this.beamingRuleLookups.clear(); + this._barRendererLookup.clear(); + + this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile)!; + this.pagePadding = this.renderer.settings.display.padding.map(p => p / this.renderer.settings.display.scale); if (!this.pagePadding) { this.pagePadding = [0, 0, 0, 0]; @@ -118,6 +125,10 @@ export abstract class ScoreLayout { private _lazyPartials: Map = new Map(); + protected getExistingPartialArgs(id:string): RenderFinishedEventArgs|undefined { + return this._lazyPartials.has(id) ? this._lazyPartials.get(id)!.args : undefined; + } + protected registerPartial(args: RenderFinishedEventArgs, callback: (canvas: ICanvas) => void) { if (args.height === 0) { return; @@ -137,7 +148,11 @@ export abstract class ScoreLayout { this._internalRenderLazyPartial(args, callback); } else { // in case of lazy loading -> first register lazy, then notify - this._lazyPartials.set(args.id, new LazyPartial(args, callback)); + const partial: LazyPartial = { + args, + renderCallback: callback + }; + this._lazyPartials.set(args.id, partial); (this.renderer.partialLayoutFinished as EventEmitterOfT).trigger(args); } } @@ -500,7 +515,7 @@ export abstract class ScoreLayout { } } - public layoutAndRenderAnnotation(y: number): number { + protected _layoutAndRenderAnnotation(y: number): number { // attention, you are not allowed to remove change this notice within any version of this library without permission! const msg: string = 'rendered by alphaTab'; const resources: RenderingResources = this.renderer.settings.display.resources; diff --git a/packages/alphatab/src/rendering/layout/VerticalLayoutBase.ts b/packages/alphatab/src/rendering/layout/VerticalLayoutBase.ts index d1b88c302..93ee2543b 100644 --- a/packages/alphatab/src/rendering/layout/VerticalLayoutBase.ts +++ b/packages/alphatab/src/rendering/layout/VerticalLayoutBase.ts @@ -1,13 +1,14 @@ +import type { EventEmitterOfT } from '@coderline/alphatab/EventEmitter'; import { Logger } from '@coderline/alphatab/Logger'; import { ScoreSubElement } from '@coderline/alphatab/model/Score'; import { type ICanvas, TextAlign } from '@coderline/alphatab/platform/ICanvas'; -import type { RenderingResources } from '@coderline/alphatab/RenderingResources'; import type { TextGlyph } from '@coderline/alphatab/rendering/glyphs/TextGlyph'; import type { RenderHints } from '@coderline/alphatab/rendering/IScoreRenderer'; import { ScoreLayout } from '@coderline/alphatab/rendering/layout/ScoreLayout'; import { RenderFinishedEventArgs } from '@coderline/alphatab/rendering/RenderFinishedEventArgs'; import type { MasterBarsRenderers } from '@coderline/alphatab/rendering/staves/MasterBarsRenderers'; import type { StaffSystem } from '@coderline/alphatab/rendering/staves/StaffSystem'; +import type { RenderingResources } from '@coderline/alphatab/RenderingResources'; /** * Base layout for page and parchment style layouts where we have an endless @@ -21,11 +22,18 @@ export abstract class VerticalLayoutBase extends ScoreLayout { private _reuseViewPort: boolean = false; + private _preSystemPartialIds: string[] = []; + private _systemPartialIds: string[] = []; + protected doLayoutAndRender(renderHints: RenderHints | undefined): void { let y: number = this.pagePadding![1]; this.width = this.renderer.width; this._allMasterBarRenderers = []; + this._preSystemPartialIds = []; + this._systemPartialIds = []; + this._reuseViewPort = renderHints?.reuseViewport ?? false; + this._systems = []; // // 1. Score Info @@ -38,11 +46,11 @@ export abstract class VerticalLayoutBase extends ScoreLayout { y = this._layoutAndRenderChordDiagrams(y, -1); // // 4. One result per StaffSystem - y = this._layoutAndRenderScore(y); + y = this._layoutAndRenderScore(y, this.firstBarIndex); y = this.layoutAndRenderBottomScoreInfo(y); - y = this.layoutAndRenderAnnotation(y); + y = this._layoutAndRenderAnnotation(y); this.height = (y + this.pagePadding![3]) * this.renderer.settings.display.scale; } @@ -52,6 +60,15 @@ export abstract class VerticalLayoutBase extends ScoreLayout { super.registerPartial(args, callback); } + protected reregisterPartial(id: string) { + const args = this.getExistingPartialArgs(id); + if (!args) { + return; + } + args.reuseViewport = this._reuseViewPort; + (this.renderer.partialLayoutFinished as EventEmitterOfT).trigger(args); + } + public get supportsResize(): boolean { return true; } @@ -64,6 +81,58 @@ export abstract class VerticalLayoutBase extends ScoreLayout { return x; } + public override doUpdateForBars(renderHints: RenderHints): boolean { + this._reuseViewPort = renderHints.reuseViewport ?? false; + const firstModifiedMasterBar = renderHints.firstChangedMasterBar!; + + // first update existing systems as needed + const systemIndex = this._systems.findIndex(s => { + const first = s.masterBarsRenderers[0].masterBar.index; + const last = s.masterBarsRenderers[s.masterBarsRenderers.length - 1].masterBar.index; + return first <= firstModifiedMasterBar && firstModifiedMasterBar <= last; + }); + + if (systemIndex === -1 || !this.renderer.settings.core.enableLazyLoading) { + return false; + } + + // Bars from the start of the re-layouted system onward will be re-registered during the + // paint pass. Clear their old entries from the preserved BoundsLookup so registration + // produces a clean, complete lookup after this render finishes. + const firstRebuiltBarIndex = this._systems[systemIndex].masterBarsRenderers[0].masterBar.index; + this.renderer.boundsLookup!.clearFromMasterBar(firstRebuiltBarIndex); + + // for now we do a full relayout from the first modified masterbar + // there is a lot of room for even more performant updates, but they come + // at a risk that features break. + // e.g. we could only shift systems where the content didn't change, + // but we might still have ties/slurs which have to be updated. + const removeSystems = this._systems.splice(systemIndex, this._systems.length - systemIndex); + this._systemPartialIds.splice(systemIndex, this._systemPartialIds.length - systemIndex); + const system = removeSystems[0]; + let y = system.y; + const firstBarIndex = system.masterBarsRenderers[0].masterBar.index; + + // signal all partials which didn't change + for (const preSystemPartial of this._preSystemPartialIds) { + this.reregisterPartial(preSystemPartial); + } + for (let i = 0; i < systemIndex; i++) { + this.reregisterPartial(this._systemPartialIds[i]); + } + + // new partials for all other prats + y = this._layoutAndRenderScore(y, firstBarIndex); + + y = this.layoutAndRenderBottomScoreInfo(y); + + y = this._layoutAndRenderAnnotation(y); + + this.height = (y + this.pagePadding![3]) * this.renderer.settings.display.scale; + + return true; + } + public doResize(): void { let y: number = this.pagePadding![1]; this.width = this.renderer.width; @@ -85,7 +154,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { y = this.layoutAndRenderBottomScoreInfo(y); - y = this.layoutAndRenderAnnotation(y); + y = this._layoutAndRenderAnnotation(y); this.height = (y + this.pagePadding![3]) * this.renderer.settings.display.scale; } @@ -115,6 +184,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { canvas.textAlign = TextAlign.Center; this.tuningGlyph!.paint(0, 0, canvas); }); + this._preSystemPartialIds.push(e.id); return y + tuningHeight; } @@ -143,6 +213,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { canvas.textAlign = TextAlign.Center; this.chordDiagrams!.paint(0, 0, canvas); }); + this._preSystemPartialIds.push(e.id); return y + diagramHeight; } @@ -197,6 +268,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { g.paint(0, 0, canvas); } }); + this._preSystemPartialIds.push(e.id); } return y + infoHeight; @@ -205,6 +277,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { private _resizeAndRenderScore(y: number, oldHeight: number): number { // if we have a fixed number of bars per row, we only need to refit them. const barsPerRowActive = this.getBarsPerSystem(0) > 0; + this._systemPartialIds = []; if (barsPerRowActive) { for (let i: number = 0; i < this._systems.length; i++) { const system: StaffSystem = this._systems[i]; @@ -270,12 +343,10 @@ export abstract class VerticalLayoutBase extends ScoreLayout { return y; } - private _layoutAndRenderScore(y: number): number { - const startIndex: number = this.firstBarIndex; + private _layoutAndRenderScore(y: number, startIndex: number): number { let currentBarIndex: number = startIndex; const endBarIndex: number = this.lastBarIndex; - this._systems = []; while (currentBarIndex <= endBarIndex) { // create system and align set proper coordinates const system: StaffSystem = this._createStaffSystem(currentBarIndex, endBarIndex); @@ -317,6 +388,7 @@ export abstract class VerticalLayoutBase extends ScoreLayout { // since we use partial drawing system.paint(0, -(args.y / this.renderer.settings.display.scale), canvas); }); + this._systemPartialIds.push(args.id); // calculate coordinates for next system return height; @@ -326,6 +398,11 @@ export abstract class VerticalLayoutBase extends ScoreLayout { * Realignes the bars in this line according to the available space */ private _fitSystem(system: StaffSystem): void { + // If a bar added late in the assembly introduced a shorter note than earlier bars, the + // earlier bars' spring constants (and the cached system widths / totals) are stale. + // Reconcile now - it's a no-op when nothing changed. + system.reconcileMinDurationIfDirty(); + if (system.isFull || system.width > this._maxWidth || this.renderer.settings.display.justifyLastSystem) { this._scaleToWidth(system, this._maxWidth); } else { @@ -334,40 +411,51 @@ export abstract class VerticalLayoutBase extends ScoreLayout { system.finalizeSystem(); } + /** + * Whether the layout honours the model's {@link Bar.displayScale} when distributing staff width. + * When `true` (Parchment, Page with `SystemsLayoutMode.UseModelLayout`), bars are weighted by + * `displayScale`. When `false` (Page with `SystemsLayoutMode.Automatic`), `displayScale` is ignored + * and bars are weighted by their natural content width produced by the built-in spacing engine. + * Prefix/postfix overhead (clef, key sig, time sig, barlines) is treated as fixed in both modes. + */ protected abstract get shouldApplyBarScale(): boolean; private _scaleToWidth(system: StaffSystem, width: number): void { const staffWidth = width - system.accoladeWidth; const shouldApplyBarScale = this.shouldApplyBarScale; - const totalScale = system.totalBarDisplayScale; - - // NOTE: it currently delivers best results if we evenly distribute the available space across bars - // scaling bars relatively to their computed width, rather causes distortions whenever bars have - // pre-beat glyphs. - - // most precise scaling would come if we use the contents (voiceContainerGlyph) width as a calculation - // factor. but this would make the calculation additionally complex with not much gain. - - const difference: number = width - system.computedWidth; - const spacePerBar: number = difference / system.masterBarsRenderers.length; + // Industry fixed-overhead model (Behind Bars, Dorico, Finale, Sibelius, MuseScore, Guitar Pro): + // prefix/postfix glyphs (clef, key sig, time sig, barlines) are treated as fixed overhead and the + // remaining staff width is distributed across bars by a per-bar weight. + // + // distributable = staffWidth - totalFixedOverhead + // contentShare = distributable / sum(weight) + // bar.width = bar.maxFixedOverhead + weight * contentShare + // + // The weight depends on the layout mode: + // - shouldApplyBarScale=true -> weight = bar.displayScale (model-driven, matches Guitar Pro) + // displayScale defaults to 1, so an unset value behaves identically + // to an explicit 1 (GP omits the property when not customized). + // - shouldApplyBarScale=false -> weight = natural content width (automatic, ignores displayScale) + // + // Per-bar maxFixedOverhead / maxContentWidth and the system-wide totals are maintained incrementally + // in StaffSystem._applyLayoutAndUpdateWidth / revertLastBar so this pass can apply directly. + const weightTotal = shouldApplyBarScale ? system.totalBarDisplayScale : system.totalContentWidth; + const distributable = Math.max(0, staffWidth - system.totalFixedOverhead); + const contentShare = weightTotal > 0 ? distributable / weightTotal : 0; for (const s of system.allStaves) { s.resetSharedLayoutData(); - // scale the bars by keeping their respective ratio size let w = 0; - for (const renderer of s.barRenderers) { + for (let i = 0; i < s.barRenderers.length; i++) { + const renderer = s.barRenderers[i]; + const mb = system.masterBarsRenderers[i]; renderer.x = w; renderer.y = s.topPadding + s.topOverflow; - let actualBarWidth: number; - if (shouldApplyBarScale) { - const barDisplayScale = system.getBarDisplayScale(renderer); - actualBarWidth = (barDisplayScale * staffWidth) / totalScale; - } else { - actualBarWidth = renderer.computedWidth + spacePerBar; - } + const weight = shouldApplyBarScale ? system.getBarDisplayScale(renderer) : mb.maxContentWidth; + const actualBarWidth = mb.maxFixedOverhead + weight * contentShare; renderer.scaleToWidth(actualBarWidth); w += renderer.width; diff --git a/packages/alphatab/src/rendering/staves/BarLayoutingInfo.ts b/packages/alphatab/src/rendering/staves/BarLayoutingInfo.ts index 2e3e89a19..3333f03ff 100644 --- a/packages/alphatab/src/rendering/staves/BarLayoutingInfo.ts +++ b/packages/alphatab/src/rendering/staves/BarLayoutingInfo.ts @@ -46,6 +46,24 @@ export class BarLayoutingInfo { public minStretchForce: number = 0; public totalSpringConstant: number = 0; + /** + * The smallest note duration encountered within this bar's springs, used as the reference in + * the Gourlay stretch formula. Read by the owning {@link StaffSystem} so that the system can + * aggregate a shared minimum across all bars and trigger a reconcile if an added bar introduces + * a shorter duration than previously seen. + */ + public get localMinDuration(): number { + return this._minDuration; + } + + /** + * The minimum-duration reference against which the spring constants currently held by this info + * were computed. Set by {@link finish} and {@link recomputeSpringConstants}. The owning + * StaffSystem compares this against its system-wide minimum to decide whether spring constants + * need re-derivation. + */ + public computedWithMinDuration: number = 0; + private _updateMinStretchForce(force: number): void { if (this.minStretchForce < force) { this.minStretchForce = force; @@ -238,11 +256,28 @@ export class BarLayoutingInfo { } } - this._calculateSpringConstants(); + this._calculateSpringConstants(this._minDuration); + this.computedWithMinDuration = this._minDuration; + this.version++; + } + + /** + * Re-derives the spring constants (and {@link minStretchForce} / {@link totalSpringConstant}) + * using a caller-supplied minimum-duration reference rather than this bar's local minimum. + * + * Called by {@link StaffSystem.reconcileMinDurationIfDirty} when a bar added later to the + * system introduced a shorter note than previously seen, invalidating this bar's spring + * constants. Grace-rod data is not recomputed — it is independent of the minimum-duration + * reference. The internal {@link version} is bumped so downstream consumers (e.g. + * {@link BarRendererBase.applyLayoutingInfo}) pick up the refreshed positions. + */ + public recomputeSpringConstants(minDuration: number): void { + this._calculateSpringConstants(minDuration); + this.computedWithMinDuration = minDuration; this.version++; } - private _calculateSpringConstants(): void { + private _calculateSpringConstants(minDuration: number): void { let totalSpringConstant: number = 0; const sortedSprings: Spring[] = this._timeSortedSprings; if (sortedSprings.length === 0) { @@ -259,7 +294,7 @@ export class BarLayoutingInfo { const nextSpring: Spring = sortedSprings[i + 1]; duration = Math.abs(nextSpring.timePosition - currentSpring.timePosition); } - currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration); + currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration, minDuration); totalSpringConstant += 1 / currentSpring.springConstant; } this.totalSpringConstant = 1 / totalSpringConstant; @@ -334,7 +369,7 @@ export class BarLayoutingInfo { // } // } - private _calculateSpringConstant(spring: Spring, duration: number): number { + private _calculateSpringConstant(spring: Spring, duration: number, minDuration: number): number { if (duration <= 0) { duration = MidiUtils.toTicks(Duration.TwoHundredFiftySixth); } @@ -343,7 +378,6 @@ export class BarLayoutingInfo { } const smallestDuration: number = spring.smallestDuration; - const minDuration = this._minDuration; const minDurationWidth = BarLayoutingInfo._defaultMinDurationWidth; const phi: number = 1 + 0.85 * Math.log2(duration / minDuration); diff --git a/packages/alphatab/src/rendering/staves/MasterBarsRenderers.ts b/packages/alphatab/src/rendering/staves/MasterBarsRenderers.ts index 186a4734e..57a321122 100644 --- a/packages/alphatab/src/rendering/staves/MasterBarsRenderers.ts +++ b/packages/alphatab/src/rendering/staves/MasterBarsRenderers.ts @@ -14,6 +14,20 @@ export class MasterBarsRenderers { public masterBar!: MasterBar; public additionalMultiBarRestIndexes: number[] | null = null; + /** + * Max fixed overhead (prefix + postfix glyph width) across all staves of this bar. + * Used by the layout-mode horizontal scaling pass to carve out the fixed-overhead bucket + * before distributing staff width across bars. + */ + public maxFixedOverhead: number = 0; + + /** + * Max natural content width (computedWidth - fixedOverhead) across all staves of this bar. + * Used as the bar weight when the layout ignores {@link MasterBar.displayScale} (e.g. + * Page layout with `SystemsLayoutMode.Automatic`). + */ + public maxContentWidth: number = 0; + public get lastMasterBarIndex(): number { if (this.additionalMultiBarRestIndexes) { return this.additionalMultiBarRestIndexes[this.additionalMultiBarRestIndexes.length - 1]; diff --git a/packages/alphatab/src/rendering/staves/StaffSystem.ts b/packages/alphatab/src/rendering/staves/StaffSystem.ts index 71af4a29e..1fa922a1f 100644 --- a/packages/alphatab/src/rendering/staves/StaffSystem.ts +++ b/packages/alphatab/src/rendering/staves/StaffSystem.ts @@ -203,6 +203,50 @@ export class StaffSystem { */ public totalBarDisplayScale: number = 0; + /** + * Sum of per-bar {@link MasterBarsRenderers.maxFixedOverhead} across the system. The layout-mode + * horizontal scaling pass subtracts this from the available staff width before distributing the + * remainder across bars. + */ + public totalFixedOverhead: number = 0; + + /** + * Sum of per-bar {@link MasterBarsRenderers.maxContentWidth} across the system. Used as the + * denominator when distributing staff width in modes that weight bars by natural content width + * (Page layout with `SystemsLayoutMode.Automatic`). + */ + public totalContentWidth: number = 0; + + /** + * Shortest note duration (in ticks) across every bar that has been added to this system, used + * as the common reference in the Gourlay stretch formula so that rhythmically-equivalent beats + * in different bars of the same system align column-wise. + * + * `-1` means "no bar added yet". The value only moves downward during system assembly; when a + * new bar introduces a shorter minimum, {@link isMinDurationDirty} is set so that + * {@link reconcileMinDurationIfDirty} can re-derive spring constants on the previously-added + * bars before layout distribution runs. + */ + public minDuration: number = -1; + + /** + * Set when a bar added to this system introduced a shorter {@link minDuration} than previously + * seen, leaving earlier bars' spring constants stale. Consumed by + * {@link reconcileMinDurationIfDirty} which is called from `VerticalLayoutBase._fitSystem` + * once the system is fully assembled. + */ + public isMinDurationDirty: boolean = false; + + /** + * Whether this system coordinates a shared minimum-duration reference across its bars for the + * Gourlay stretch formula. Defaults to `true` for page-style and parchment layouts where bars + * of a system fight for a common staff width. Set to `false` for horizontal layouts where each + * bar is sized independently (by `bar.displayWidth` or its intrinsic width) and there is no + * column-alignment concern - each bar keeps its local minimum so pre-existing rendering is + * preserved. + */ + public shareMinDurationAcrossBars: boolean = true; + public isLast: boolean = false; public masterBarsRenderers: MasterBarsRenderers[] = []; public staves: StaffTrackGroup[] = []; @@ -278,6 +322,10 @@ export class StaffSystem { this.firstVisibleStaff = firstVisibleStaff; this._calculateAccoladeSpacing(tracks); + // On the resize path the layoutingInfo was finalized in a previous layout pass, so we + // only need to check whether its min-duration reference still matches the new system's. + this._trackSystemMinDuration(renderers.layoutingInfo); + this._applyLayoutAndUpdateWidth(); return renderers; } @@ -349,12 +397,99 @@ export class StaffSystem { this._calculateAccoladeSpacing(tracks); barLayoutingInfo.finish(); + // Reconcile against the system-wide minimum-duration reference now that springs are + // finalized. If this bar introduced a shorter note, earlier bars become stale (flagged + // for bulk reconcile at fit time). If the system already had a shorter min than this + // bar's local one, this bar's spring constants are recomputed immediately so the width + // we return below reflects the shared reference. + this._trackSystemMinDuration(barLayoutingInfo); + // ensure same widths of new renderer result.width = this._applyLayoutAndUpdateWidth(); return result; } + /** + * Updates {@link minDuration} and {@link isMinDurationDirty} when a bar is added, and brings + * the just-added bar's {@link BarLayoutingInfo} in line with the current system minimum if the + * system already saw a shorter reference. The bulk reconcile over previously-added bars is + * deferred to {@link reconcileMinDurationIfDirty} (called from `_fitSystem`) to avoid + * re-iterating the system every time a bar is appended. + */ + private _trackSystemMinDuration(info: BarLayoutingInfo): void { + if (!this.shareMinDurationAcrossBars) { + return; + } + const localMin = info.localMinDuration; + if (this.minDuration === -1 || localMin < this.minDuration) { + // this bar shortens the system minimum; earlier bars (if any) are now stale + if (this.masterBarsRenderers.length > 1 && localMin !== this.minDuration) { + this.isMinDurationDirty = true; + } + this.minDuration = localMin; + } + if (info.computedWithMinDuration > this.minDuration) { + // this bar was initialized against a larger (local) min than the system carries; pull + // it down to the system reference so its computedWidth reflects the shared spacing. + info.recomputeSpringConstants(this.minDuration); + } + } + + /** + * Re-derives spring constants on bars whose {@link BarLayoutingInfo.computedWithMinDuration} + * is out of sync with the current {@link minDuration}, and rebuilds the cached system totals + * (widths, {@link totalFixedOverhead}, {@link totalContentWidth}) from the refreshed bar + * widths. Called from `VerticalLayoutBase._fitSystem` after the system is fully assembled and + * before distribution runs. No-op when {@link isMinDurationDirty} is false. + */ + public reconcileMinDurationIfDirty(): void { + if (!this.isMinDurationDirty) { + return; + } + + let systemWidth = this.accoladeWidth; + let totalFixedOverhead = 0; + let totalContentWidth = 0; + + for (const mb of this.masterBarsRenderers) { + if (mb.layoutingInfo.computedWithMinDuration > this.minDuration) { + mb.layoutingInfo.recomputeSpringConstants(this.minDuration); + } + + let maxPrefix = 0; + let maxContent = 0; + let realWidth = 0; + for (const r of mb.renderers) { + r.applyLayoutingInfo(); + if (r.computedWidth > realWidth) { + realWidth = r.computedWidth; + } + const overhead = r.fixedOverhead; + if (overhead > maxPrefix) { + maxPrefix = overhead; + } + const content = Math.max(0, r.computedWidth - overhead); + if (content > maxContent) { + maxContent = content; + } + } + + mb.maxFixedOverhead = maxPrefix; + mb.maxContentWidth = maxContent; + mb.width = realWidth; + systemWidth += realWidth; + totalFixedOverhead += maxPrefix; + totalContentWidth += maxContent; + } + + this.width = systemWidth; + this.computedWidth = systemWidth; + this.totalFixedOverhead = totalFixedOverhead; + this.totalContentWidth = totalContentWidth; + this.isMinDurationDirty = false; + } + public getBarDisplayScale(renderer: BarRendererBase) { return this.staves.length > 1 ? renderer.bar.masterBar.displayScale : renderer.bar.displayScale; } @@ -400,6 +535,8 @@ export class StaffSystem { this.width -= width; this.computedWidth -= width; this.totalBarDisplayScale -= barDisplayScale; + this.totalFixedOverhead -= toRemove.maxFixedOverhead; + this.totalContentWidth -= toRemove.maxContentWidth; return toRemove; } return null; @@ -407,6 +544,8 @@ export class StaffSystem { private _applyLayoutAndUpdateWidth(): number { let realWidth: number = 0; + let maxFixedOverhead: number = 0; + let maxContentWidth: number = 0; let barDisplayScale = 0; for (const s of this.allStaves) { @@ -418,9 +557,24 @@ export class StaffSystem { if (last.computedWidth > realWidth) { realWidth = last.computedWidth; } + + const overhead = last.fixedOverhead; + if (overhead > maxFixedOverhead) { + maxFixedOverhead = overhead; + } + const content = Math.max(0, last.computedWidth - overhead); + if (content > maxContentWidth) { + maxContentWidth = content; + } } + const renderers = this.masterBarsRenderers[this.masterBarsRenderers.length - 1]; + renderers.maxFixedOverhead = maxFixedOverhead; + renderers.maxContentWidth = maxContentWidth; + this.totalBarDisplayScale += barDisplayScale; + this.totalFixedOverhead += maxFixedOverhead; + this.totalContentWidth += maxContentWidth; this.width += realWidth; this.computedWidth += realWidth; diff --git a/packages/alphatab/src/rendering/utils/BoundsLookup.ts b/packages/alphatab/src/rendering/utils/BoundsLookup.ts index 396a7837f..ce8a91cbd 100644 --- a/packages/alphatab/src/rendering/utils/BoundsLookup.ts +++ b/packages/alphatab/src/rendering/utils/BoundsLookup.ts @@ -169,6 +169,62 @@ export class BoundsLookup { this.isFinished = true; } + /** + * Re-opens the lookup for registrations without discarding previously registered bounds. + * Used by the renderer when it preserves this lookup across a partial render so that new + * bounds for the re-layouted range can be added while preserved systems stay intact. + * @internal + */ + public resetForPartialUpdate(): void { + this.isFinished = false; + } + + /** + * Removes all entries belonging to the given master bar index and any bars after it. + * Used before a partial render re-registers bounds for the re-layouted range, so the + * preserved lookup ends up with only the unchanged entries when registration begins. + * + * Assumes the layout aligns its re-layouted range to system boundaries - i.e. the first + * system to clear starts exactly at `masterBarIndex`. Caller is responsible for passing + * the first master-bar-index of the first re-layouted system. + * @internal + */ + public clearFromMasterBar(masterBarIndex: number): void { + // drop staff systems whose bars start at or after the cleared range. + let firstRemovedSystem = -1; + for (let i = 0; i < this.staffSystems.length; i++) { + const systemBars = this.staffSystems[i].bars; + if (systemBars.length > 0 && systemBars[0].index >= masterBarIndex) { + firstRemovedSystem = i; + break; + } + } + if (firstRemovedSystem !== -1) { + this.staffSystems.splice(firstRemovedSystem, this.staffSystems.length - firstRemovedSystem); + } + + // drop master bar entries at or beyond the cleared range. + for (const key of Array.from(this._masterBarLookup.keys())) { + if (key >= masterBarIndex) { + this._masterBarLookup.delete(key); + } + } + + // drop beat entries whose beats belong to cleared bars. + for (const key of Array.from(this._beatLookup.keys())) { + const list = this._beatLookup.get(key)!; + const filtered = list.filter(b => b.beat.voice.bar.index < masterBarIndex); + if (filtered.length === 0) { + this._beatLookup.delete(key); + } else if (filtered.length !== list.length) { + this._beatLookup.set(key, filtered); + } + } + + // drop the in-progress pointer - the next addStaffSystem call will replace it. + this._currentStaffSystem = null; + } + /** * Adds a new staff sytem to the lookup. * @param bounds The staff system bounds to add. diff --git a/packages/alphatab/src/rendering/utils/StaffSystemBounds.ts b/packages/alphatab/src/rendering/utils/StaffSystemBounds.ts index 83f0645c0..a66232bfd 100644 --- a/packages/alphatab/src/rendering/utils/StaffSystemBounds.ts +++ b/packages/alphatab/src/rendering/utils/StaffSystemBounds.ts @@ -34,15 +34,27 @@ export class StaffSystemBounds { public boundsLookup!: BoundsLookup; /** - * Finished the lookup for optimized access. + * Whether this system's bounds have already been scaled via `finish`. Prevents double-scaling + * when the parent `BoundsLookup` is preserved across partial renders and `finish` is invoked + * again on a mix of already-scaled (preserved) and newly-registered (natural-coordinate) systems. + */ + public isFinished: boolean = false; + + /** + * Finished the lookup for optimized access. Idempotent: once finished, further calls are no-ops + * so preserved systems survive partial renders without being re-scaled. */ public finish(scale: number = 1): void { + if (this.isFinished) { + return; + } this.realBounds.scaleWith(scale); this.visualBounds.scaleWith(scale); for (const t of this.bars) { t.finish(scale); } + this.isFinished = true; } /** diff --git a/packages/alphatab/src/synth/AlphaSynth.ts b/packages/alphatab/src/synth/AlphaSynth.ts index e20f07332..13a7e4e32 100644 --- a/packages/alphatab/src/synth/AlphaSynth.ts +++ b/packages/alphatab/src/synth/AlphaSynth.ts @@ -294,15 +294,14 @@ export class AlphaSynthBase implements IAlphaSynth { this._notPlayedSamples += samples.length; this.output.addSamples(samples); - - // if the sequencer finished, we instantly force a noteOff on all - // voices to complete playback and stop voices fast. + // if the sequencer finished, we instantly force a noteOff on all + // voices to complete playback and stop voices fast. // Doing this in the samplePlayed callback is too late as we might // continue generating audio for long-release notes (especially percussion like cymbals) - + // we still have checkForFinish which takes care of the counterpart - // on the sample played area to ensure we seek back. - // but thanks to this code we ensure the output will complete fast as we won't + // on the sample played area to ensure we seek back. + // but thanks to this code we ensure the output will complete fast as we won't // be adding more samples beside a 0.1s ramp-down if (this.sequencer.isFinished) { this.synthesizer.noteOffAll(true); @@ -403,6 +402,9 @@ export class AlphaSynthBase implements IAlphaSynth { this._notPlayedSamples = 0; this.output.resetSamples(); + this.output.activate(); + this._synthStopping = false; + this.state = PlayerState.Playing; this.output.play(); } @@ -577,6 +579,7 @@ export class AlphaSynthBase implements IAlphaSynth { private _stopOneTimeMidi() { this.output.pause(); + this.output.resetSamples(); this.synthesizer.noteOffAll(true); this.sequencer.resetOneTimeMidi(); this.timePosition = this.sequencer.currentTime; diff --git a/packages/alphatab/src/zip/ZipReader.ts b/packages/alphatab/src/zip/ZipReader.ts index 1c929bf61..bf28690ba 100644 --- a/packages/alphatab/src/zip/ZipReader.ts +++ b/packages/alphatab/src/zip/ZipReader.ts @@ -1,6 +1,6 @@ import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { IOHelper } from '@coderline/alphatab/io/IOHelper'; -import type { IReadable } from '@coderline/alphatab/io/IReadable'; +import { OverflowError, type IReadable } from '@coderline/alphatab/io/IReadable'; import { Inflate } from '@coderline/alphatab/zip/Inflate'; import { ZipEntry } from '@coderline/alphatab/zip/ZipEntry'; @@ -9,9 +9,11 @@ import { ZipEntry } from '@coderline/alphatab/zip/ZipEntry'; */ export class ZipReader { private _readable: IReadable; + private _maxDecodingBufferSize: number; - public constructor(readable: IReadable) { + public constructor(readable: IReadable, maxDecodingBufferSize: number) { this._readable = readable; + this._maxDecodingBufferSize = maxDecodingBufferSize; } public read(): ZipEntry[] { @@ -48,8 +50,16 @@ export class ZipReader { IOHelper.readInt32LE(readable); // compressed size const uncompressedSize: number = IOHelper.readInt32LE(readable); + if (uncompressedSize > this._maxDecodingBufferSize) { + throw new OverflowError(`Zip contains files exceeding the configured maxDecodingBufferSize`); + } const fileNameLength: number = IOHelper.readInt16LE(readable); + if (fileNameLength > this._maxDecodingBufferSize) { + throw new OverflowError(`Zip contains file names exceeding the configured maxDecodingBufferSize`); + } + const extraFieldLength: number = IOHelper.readInt16LE(readable); + const fname: string = IOHelper.toString(IOHelper.readByteArray(readable, fileNameLength), 'utf-8'); readable.skip(extraFieldLength); @@ -62,6 +72,11 @@ export class ZipReader { while (true) { const bytes: number = z.readBytes(buffer, 0, buffer.length); target.write(buffer, 0, bytes); + if (target.length > this._maxDecodingBufferSize) { + throw new OverflowError( + `Zip entry "${fname}" contains data exceeding the configured maxDecodingBufferSize` + ); + } if (bytes < buffer.length) { break; } diff --git a/packages/alphatab/test-data/corrupt/healthy.gp b/packages/alphatab/test-data/corrupt/healthy.gp new file mode 100644 index 000000000..68f509ecf Binary files /dev/null and b/packages/alphatab/test-data/corrupt/healthy.gp differ diff --git a/packages/alphatab/test-data/corrupt/healthy.gp5 b/packages/alphatab/test-data/corrupt/healthy.gp5 new file mode 100644 index 000000000..63f1bad4a Binary files /dev/null and b/packages/alphatab/test-data/corrupt/healthy.gp5 differ diff --git a/packages/alphatab/test-data/guitarpro3/beat-harmonics.gp3 b/packages/alphatab/test-data/guitarpro3/beat-harmonics.gp3 new file mode 100644 index 000000000..60ce96e05 Binary files /dev/null and b/packages/alphatab/test-data/guitarpro3/beat-harmonics.gp3 differ diff --git a/packages/alphatab/test-data/guitarpro4/harmonic-types.gp4 b/packages/alphatab/test-data/guitarpro4/harmonic-types.gp4 new file mode 100644 index 000000000..e17cf6ce7 Binary files /dev/null and b/packages/alphatab/test-data/guitarpro4/harmonic-types.gp4 differ diff --git a/packages/alphatab/test-data/guitarpro5/chord-name-overflow.gp5 b/packages/alphatab/test-data/guitarpro5/chord-name-overflow.gp5 new file mode 100755 index 000000000..8de558027 Binary files /dev/null and b/packages/alphatab/test-data/guitarpro5/chord-name-overflow.gp5 differ diff --git a/packages/alphatab/test-data/guitarpro5/harmonic-types.gp5 b/packages/alphatab/test-data/guitarpro5/harmonic-types.gp5 new file mode 100644 index 000000000..3704d72ea Binary files /dev/null and b/packages/alphatab/test-data/guitarpro5/harmonic-types.gp5 differ diff --git a/packages/alphatab/test-data/guitarpro8/orphan-tempo-automation.gp b/packages/alphatab/test-data/guitarpro8/orphan-tempo-automation.gp new file mode 100755 index 000000000..76543653b Binary files /dev/null and b/packages/alphatab/test-data/guitarpro8/orphan-tempo-automation.gp differ diff --git a/packages/alphatab/test-data/musicxml-samples/BeetAnGeSample.png b/packages/alphatab/test-data/musicxml-samples/BeetAnGeSample.png index f2fed3e79..11680dd98 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/BeetAnGeSample.png and b/packages/alphatab/test-data/musicxml-samples/BeetAnGeSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/Binchois.png b/packages/alphatab/test-data/musicxml-samples/Binchois.png index 0f280a882..98c59937a 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/Binchois.png and b/packages/alphatab/test-data/musicxml-samples/Binchois.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/BrahWiMeSample.png b/packages/alphatab/test-data/musicxml-samples/BrahWiMeSample.png index c8f6f933e..a4258a4db 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/BrahWiMeSample.png and b/packages/alphatab/test-data/musicxml-samples/BrahWiMeSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/BrookeWestSample.png b/packages/alphatab/test-data/musicxml-samples/BrookeWestSample.png index 91c991471..e8a309eeb 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/BrookeWestSample.png and b/packages/alphatab/test-data/musicxml-samples/BrookeWestSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/DebuMandSample.png b/packages/alphatab/test-data/musicxml-samples/DebuMandSample.png index 38a3af5ce..743b5ba81 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/DebuMandSample.png and b/packages/alphatab/test-data/musicxml-samples/DebuMandSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/Dichterliebe01.png b/packages/alphatab/test-data/musicxml-samples/Dichterliebe01.png index f22306ee3..1d8480bd6 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/Dichterliebe01.png and b/packages/alphatab/test-data/musicxml-samples/Dichterliebe01.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/Echigo.png b/packages/alphatab/test-data/musicxml-samples/Echigo.png index 5e18fb18c..ebefe64d5 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/Echigo.png and b/packages/alphatab/test-data/musicxml-samples/Echigo.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/FaurReveSample.png b/packages/alphatab/test-data/musicxml-samples/FaurReveSample.png index 78cef85d2..887ced6d4 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/FaurReveSample.png and b/packages/alphatab/test-data/musicxml-samples/FaurReveSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/MahlFaGe4Sample.png b/packages/alphatab/test-data/musicxml-samples/MahlFaGe4Sample.png index 52d16a8db..764df91de 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/MahlFaGe4Sample.png and b/packages/alphatab/test-data/musicxml-samples/MahlFaGe4Sample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/MozaChloSample.png b/packages/alphatab/test-data/musicxml-samples/MozaChloSample.png index 8e2c25d69..3aeb1edbe 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/MozaChloSample.png and b/packages/alphatab/test-data/musicxml-samples/MozaChloSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/MozaVeilSample.png b/packages/alphatab/test-data/musicxml-samples/MozaVeilSample.png index 6a2f58a66..39ac07349 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/MozaVeilSample.png and b/packages/alphatab/test-data/musicxml-samples/MozaVeilSample.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/MozartPianoSonata.png b/packages/alphatab/test-data/musicxml-samples/MozartPianoSonata.png index 05731c5b6..857c82649 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/MozartPianoSonata.png and b/packages/alphatab/test-data/musicxml-samples/MozartPianoSonata.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/MozartTrio.png b/packages/alphatab/test-data/musicxml-samples/MozartTrio.png index 25df1ffab..5081ac08b 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/MozartTrio.png and b/packages/alphatab/test-data/musicxml-samples/MozartTrio.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/Saltarello.png b/packages/alphatab/test-data/musicxml-samples/Saltarello.png index 2e0c78e19..4684f76cb 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/Saltarello.png and b/packages/alphatab/test-data/musicxml-samples/Saltarello.png differ diff --git a/packages/alphatab/test-data/musicxml-samples/Telemann.png b/packages/alphatab/test-data/musicxml-samples/Telemann.png index 03080dce7..166555266 100644 Binary files a/packages/alphatab/test-data/musicxml-samples/Telemann.png and b/packages/alphatab/test-data/musicxml-samples/Telemann.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/01a-Pitches-Pitches.png b/packages/alphatab/test-data/musicxml-testsuite/01a-Pitches-Pitches.png index 14a9691a8..177d85257 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/01a-Pitches-Pitches.png and b/packages/alphatab/test-data/musicxml-testsuite/01a-Pitches-Pitches.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/01b-Pitches-Intervals.png b/packages/alphatab/test-data/musicxml-testsuite/01b-Pitches-Intervals.png index 67c910786..adadef622 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/01b-Pitches-Intervals.png and b/packages/alphatab/test-data/musicxml-testsuite/01b-Pitches-Intervals.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/02a-Rests-Durations.png b/packages/alphatab/test-data/musicxml-testsuite/02a-Rests-Durations.png index d2e7bde08..2ce986fdc 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/02a-Rests-Durations.png and b/packages/alphatab/test-data/musicxml-testsuite/02a-Rests-Durations.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.png b/packages/alphatab/test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.png index db959cdfc..fedb0ed75 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.png and b/packages/alphatab/test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/03a-Rhythm-Durations.png b/packages/alphatab/test-data/musicxml-testsuite/03a-Rhythm-Durations.png index 55d0af4bf..44aec70d9 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/03a-Rhythm-Durations.png and b/packages/alphatab/test-data/musicxml-testsuite/03a-Rhythm-Durations.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.png b/packages/alphatab/test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.png index 61d10ac7b..0f34f66b9 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.png and b/packages/alphatab/test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/11a-TimeSignatures.png b/packages/alphatab/test-data/musicxml-testsuite/11a-TimeSignatures.png index 618ecfb1f..6cb2e6039 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/11a-TimeSignatures.png and b/packages/alphatab/test-data/musicxml-testsuite/11a-TimeSignatures.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/12a-Clefs.png b/packages/alphatab/test-data/musicxml-testsuite/12a-Clefs.png index a12075ae3..c6ebaf8a2 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/12a-Clefs.png and b/packages/alphatab/test-data/musicxml-testsuite/12a-Clefs.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/13a-KeySignatures.png b/packages/alphatab/test-data/musicxml-testsuite/13a-KeySignatures.png index 9e50b7255..77a288384 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/13a-KeySignatures.png and b/packages/alphatab/test-data/musicxml-testsuite/13a-KeySignatures.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/13e-KeySignatures-Cancel.png b/packages/alphatab/test-data/musicxml-testsuite/13e-KeySignatures-Cancel.png index 10de53be0..776b11fbb 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/13e-KeySignatures-Cancel.png and b/packages/alphatab/test-data/musicxml-testsuite/13e-KeySignatures-Cancel.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/22a-Noteheads.png b/packages/alphatab/test-data/musicxml-testsuite/22a-Noteheads.png index 42ccae389..f0081de94 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/22a-Noteheads.png and b/packages/alphatab/test-data/musicxml-testsuite/22a-Noteheads.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/22b-Staff-Notestyles.png b/packages/alphatab/test-data/musicxml-testsuite/22b-Staff-Notestyles.png index 5eaced5a7..1faf1f230 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/22b-Staff-Notestyles.png and b/packages/alphatab/test-data/musicxml-testsuite/22b-Staff-Notestyles.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.png b/packages/alphatab/test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.png index bc5cf3611..c2a7e0089 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.png and b/packages/alphatab/test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/23e-Tuplets-Tremolo.png b/packages/alphatab/test-data/musicxml-testsuite/23e-Tuplets-Tremolo.png index 5cc27b897..671b59043 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/23e-Tuplets-Tremolo.png and b/packages/alphatab/test-data/musicxml-testsuite/23e-Tuplets-Tremolo.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/31a-Directions.png b/packages/alphatab/test-data/musicxml-testsuite/31a-Directions.png index 1bcc43e50..7b3d17e63 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/31a-Directions.png and b/packages/alphatab/test-data/musicxml-testsuite/31a-Directions.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/31d-Directions-Compounds.png b/packages/alphatab/test-data/musicxml-testsuite/31d-Directions-Compounds.png index ef49db479..96fe71ee5 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/31d-Directions-Compounds.png and b/packages/alphatab/test-data/musicxml-testsuite/31d-Directions-Compounds.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/32a-Notations.png b/packages/alphatab/test-data/musicxml-testsuite/32a-Notations.png index 844219ead..a00ba3205 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/32a-Notations.png and b/packages/alphatab/test-data/musicxml-testsuite/32a-Notations.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/33a-Spanners.png b/packages/alphatab/test-data/musicxml-testsuite/33a-Spanners.png index 52b778cc0..8e0d7c4ca 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/33a-Spanners.png and b/packages/alphatab/test-data/musicxml-testsuite/33a-Spanners.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/33h-Spanners-Glissando.png b/packages/alphatab/test-data/musicxml-testsuite/33h-Spanners-Glissando.png index 7f4c838bb..5a19f5e29 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/33h-Spanners-Glissando.png and b/packages/alphatab/test-data/musicxml-testsuite/33h-Spanners-Glissando.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/33i-Ties-NotEnded.png b/packages/alphatab/test-data/musicxml-testsuite/33i-Ties-NotEnded.png index 7217ad6bd..a383c7b2b 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/33i-Ties-NotEnded.png and b/packages/alphatab/test-data/musicxml-testsuite/33i-Ties-NotEnded.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/34a-Print-Object-Spanners.png b/packages/alphatab/test-data/musicxml-testsuite/34a-Print-Object-Spanners.png index 77f508616..95fca954b 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/34a-Print-Object-Spanners.png and b/packages/alphatab/test-data/musicxml-testsuite/34a-Print-Object-Spanners.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/34b-Colors.png b/packages/alphatab/test-data/musicxml-testsuite/34b-Colors.png index b351de729..ef1854676 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/34b-Colors.png and b/packages/alphatab/test-data/musicxml-testsuite/34b-Colors.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/34c-Font-Size.png b/packages/alphatab/test-data/musicxml-testsuite/34c-Font-Size.png index 0ccdef398..2bd905f44 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/34c-Font-Size.png and b/packages/alphatab/test-data/musicxml-testsuite/34c-Font-Size.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.png b/packages/alphatab/test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.png index 176497971..334b34fcb 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.png and b/packages/alphatab/test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45c-SimpleRepeat-Nested.png b/packages/alphatab/test-data/musicxml-testsuite/45c-SimpleRepeat-Nested.png index fe851e8ff..0498d4530 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45c-SimpleRepeat-Nested.png and b/packages/alphatab/test-data/musicxml-testsuite/45c-SimpleRepeat-Nested.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45d-Repeats-MultipleEndings.png b/packages/alphatab/test-data/musicxml-testsuite/45d-Repeats-MultipleEndings.png index b1745ac09..67f4aef4f 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45d-Repeats-MultipleEndings.png and b/packages/alphatab/test-data/musicxml-testsuite/45d-Repeats-MultipleEndings.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45e-Repeats-Combination.png b/packages/alphatab/test-data/musicxml-testsuite/45e-Repeats-Combination.png index 5a77fdb96..573bd1c7c 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45e-Repeats-Combination.png and b/packages/alphatab/test-data/musicxml-testsuite/45e-Repeats-Combination.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.png b/packages/alphatab/test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.png index 156599fba..517656553 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.png and b/packages/alphatab/test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45h-Repeats-Partial.png b/packages/alphatab/test-data/musicxml-testsuite/45h-Repeats-Partial.png index b62ac1557..7eb772b5c 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45h-Repeats-Partial.png and b/packages/alphatab/test-data/musicxml-testsuite/45h-Repeats-Partial.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/45i-Repeats-Nested.png b/packages/alphatab/test-data/musicxml-testsuite/45i-Repeats-Nested.png index 665ad2a2f..75cd736ab 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/45i-Repeats-Nested.png and b/packages/alphatab/test-data/musicxml-testsuite/45i-Repeats-Nested.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/46a-Barlines.png b/packages/alphatab/test-data/musicxml-testsuite/46a-Barlines.png index 7d6a264f2..34fdc33b7 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/46a-Barlines.png and b/packages/alphatab/test-data/musicxml-testsuite/46a-Barlines.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/52a-PageLayout.png b/packages/alphatab/test-data/musicxml-testsuite/52a-PageLayout.png index 008a52a86..e43de98fb 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/52a-PageLayout.png and b/packages/alphatab/test-data/musicxml-testsuite/52a-PageLayout.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/71f-AllChordTypes.png b/packages/alphatab/test-data/musicxml-testsuite/71f-AllChordTypes.png index 2f0595d96..b3246dc73 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/71f-AllChordTypes.png and b/packages/alphatab/test-data/musicxml-testsuite/71f-AllChordTypes.png differ diff --git a/packages/alphatab/test-data/musicxml-testsuite/72c-TransposingInstruments-Change.png b/packages/alphatab/test-data/musicxml-testsuite/72c-TransposingInstruments-Change.png index 5022744dd..21a832d6f 100644 Binary files a/packages/alphatab/test-data/musicxml-testsuite/72c-TransposingInstruments-Change.png and b/packages/alphatab/test-data/musicxml-testsuite/72c-TransposingInstruments-Change.png differ diff --git a/packages/alphatab/test-data/musicxml4/percussion-articulation.xml b/packages/alphatab/test-data/musicxml4/percussion-articulation.xml new file mode 100644 index 000000000..ef7a56897 --- /dev/null +++ b/packages/alphatab/test-data/musicxml4/percussion-articulation.xml @@ -0,0 +1,51 @@ + + + + + Drums + + Drumset + + + 10 + 1 + + + + + + + 1 + + 0 + + + + percussion + + + + + D + 2 + + 1 + 1 + quarter + + + + C + 1 + 3 + + 1 + 1 + quarter + + + + diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/onnotes-beat.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/onnotes-beat.png index d954bad7a..1161cfd40 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/onnotes-beat.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/onnotes-beat.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-bar.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-bar.png index b30dd8f43..598ba0fa4 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-bar.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-bar.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-beat.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-beat.png index aa003278c..49d2b3362 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-beat.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-beat.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-master.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-master.png index d3d5ba19b..b89766cc2 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-master.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-master.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-note.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-note.png index b3d74478c..10b41fb99 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-note.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-note.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-system.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-system.png index 52d672911..2cb4c4dfa 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/real-system.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/real-system.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-bar.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-bar.png index b30dd8f43..598ba0fa4 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-bar.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-bar.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-beat.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-beat.png index 229ddeb42..f06e90c00 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-beat.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-beat.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-master.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-master.png index ca7495334..0f5d63e6c 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-master.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-master.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-note.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-note.png index b3d74478c..10b41fb99 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-note.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-note.png differ diff --git a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-system.png b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-system.png index ed849802e..5578287ee 100644 Binary files a/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-system.png and b/packages/alphatab/test-data/visual-tests/bounds-lookup/visual-system.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/bends.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/bends.png index 1eb149445..c09c64f66 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/bends.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/bends.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/chords.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/chords.png index c2ddb1693..45308b807 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/chords.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/chords.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/fingering.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/fingering.png index 30c71bffb..850ae6d5e 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/fingering.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/fingering.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-asc-then-desc.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-asc-then-desc.png new file mode 100644 index 000000000..def200d1b Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-asc-then-desc.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at1.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at1.png new file mode 100644 index 000000000..2fb19ff66 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at1.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at10.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at10.png new file mode 100644 index 000000000..4a1813295 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at10.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at11.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at11.png new file mode 100644 index 000000000..9a5255e5d Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at11.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at12.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at12.png new file mode 100644 index 000000000..d7cefbc39 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at12.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at13.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at13.png new file mode 100644 index 000000000..2004ee2ae Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at13.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at14.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at14.png new file mode 100644 index 000000000..763a58feb Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at14.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at2.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at2.png new file mode 100644 index 000000000..a58d4c608 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at2.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at3.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at3.png new file mode 100644 index 000000000..ac4426f03 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at3.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at4.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at4.png new file mode 100644 index 000000000..1c9fb1120 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at4.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at5.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at5.png new file mode 100644 index 000000000..8a2842216 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at5.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at6.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at6.png new file mode 100644 index 000000000..98790d58f Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at6.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at7.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at7.png new file mode 100644 index 000000000..14d4491e7 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at7.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at8.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at8.png new file mode 100644 index 000000000..fd2e50864 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at8.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at9.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at9.png new file mode 100644 index 000000000..929a74d4c Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-at9.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-chord-with-chain.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-chord-with-chain.png new file mode 100644 index 000000000..b64aecccd Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-chord-with-chain.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-labels-disabled.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-labels-disabled.png new file mode 100644 index 000000000..f13e35db6 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-labels-disabled.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-h-slide.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-h-slide.png new file mode 100644 index 000000000..ae8d239e0 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-h-slide.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-slide-h-p.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-slide-h-p.png new file mode 100644 index 000000000..0ac567bfe Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-mixed-slide-h-p.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-pull-off-chain.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-pull-off-chain.png new file mode 100644 index 000000000..8ad135117 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-pull-off-chain.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-score-only.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-score-only.png new file mode 100644 index 000000000..d31249a11 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-score-only.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-tab-only.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-tab-only.png new file mode 100644 index 000000000..c37ee2d66 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/hopo-arcs-tab-only.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/slides.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/slides.png index 9cdf27e2c..945992e53 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/slides.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/slides.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-1200.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-1200.png index 045603b39..859219e7f 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-1200.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-1200.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-600.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-600.png index a70d1f008..1caccd0f8 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-600.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-600.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-850.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-850.png index b99f11517..7cff47a95 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-850.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/sustain-850.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/timer.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/timer.png index feaefa6e7..8b0eb2d30 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/timer.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/timer.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-bar.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-bar.png index 0f17d8faa..77991c34f 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-bar.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-bar.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-bottom.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-bottom.png index 9eba6dd37..d71fbd0bd 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-bottom.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-bottom.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-mixed.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-mixed.png index 2161e681b..9466cbb47 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-mixed.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags-mixed.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags.png index 12a921622..6433c3946 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tremolo-flags.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/triplet-feel.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/triplet-feel.png index 86be917dc..ddf791d44 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/triplet-feel.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/triplet-feel.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png index 0efde3fcc..c675ce4ee 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png differ diff --git a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets.png b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets.png index cb388f859..f463b6c14 100644 Binary files a/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets.png and b/packages/alphatab/test-data/visual-tests/effects-and-annotations/tuplets.png differ diff --git a/packages/alphatab/test-data/visual-tests/general/colors-disabled.png b/packages/alphatab/test-data/visual-tests/general/colors-disabled.png index 3ac69a4f9..19e9ad226 100644 Binary files a/packages/alphatab/test-data/visual-tests/general/colors-disabled.png and b/packages/alphatab/test-data/visual-tests/general/colors-disabled.png differ diff --git a/packages/alphatab/test-data/visual-tests/general/colors.png b/packages/alphatab/test-data/visual-tests/general/colors.png index 50fe1ab8f..248f7247d 100644 Binary files a/packages/alphatab/test-data/visual-tests/general/colors.png and b/packages/alphatab/test-data/visual-tests/general/colors.png differ diff --git a/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png b/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png index cec2bae48..d00d9e033 100644 Binary files a/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png and b/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png differ diff --git a/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm.png b/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm.png index fa37ac2fc..0431f90f4 100644 Binary files a/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm.png and b/packages/alphatab/test-data/visual-tests/guitar-tabs/rhythm.png differ diff --git a/packages/alphatab/test-data/visual-tests/guitar-tabs/string-variations.png b/packages/alphatab/test-data/visual-tests/guitar-tabs/string-variations.png index 60a6b6cf3..0861804be 100644 Binary files a/packages/alphatab/test-data/visual-tests/guitar-tabs/string-variations.png and b/packages/alphatab/test-data/visual-tests/guitar-tabs/string-variations.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-all.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-all.png index cf361eaff..79d27cb06 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-all.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-all.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-first.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-first.png index 9ed05856f..85819c041 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-first.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-first.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-hide.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-hide.png index ef76abb22..4122aa8c1 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-hide.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-bar-override-hide.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-all.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-all.png index 2b56614e9..7d764a325 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-all.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-all.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-first.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-first.png index 3ef37bd65..3b2cf1554 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-first.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-first.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-hide.png b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-hide.png index 4489daebb..be4f06aa6 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-hide.png and b/packages/alphatab/test-data/visual-tests/layout/barnumberdisplay-stylesheet-hide.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/brackets-braces-none.png b/packages/alphatab/test-data/visual-tests/layout/brackets-braces-none.png index ecbd87a60..da6adc54d 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/brackets-braces-none.png and b/packages/alphatab/test-data/visual-tests/layout/brackets-braces-none.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves-in-first.png b/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves-in-first.png index 602e12303..ff17ffc0f 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves-in-first.png and b/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves-in-first.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves.png b/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves.png index abe0f156a..03020a1ed 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves.png and b/packages/alphatab/test-data/visual-tests/layout/hide-empty-staves.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/horizontal-layout.png b/packages/alphatab/test-data/visual-tests/layout/horizontal-layout.png index 7237389d2..c0e5e337c 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/horizontal-layout.png and b/packages/alphatab/test-data/visual-tests/layout/horizontal-layout.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/multi-system-slur-scale-up-2-700.png b/packages/alphatab/test-data/visual-tests/layout/multi-system-slur-scale-up-2-700.png index 7580008c8..868480424 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/multi-system-slur-scale-up-2-700.png and b/packages/alphatab/test-data/visual-tests/layout/multi-system-slur-scale-up-2-700.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/multi-track.png b/packages/alphatab/test-data/visual-tests/layout/multi-track.png index 7ffc82ba3..bf211092d 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/multi-track.png and b/packages/alphatab/test-data/visual-tests/layout/multi-track.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/multi-voice.png b/packages/alphatab/test-data/visual-tests/layout/multi-voice.png index 3aaeb2d84..2b2720443 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/multi-voice.png and b/packages/alphatab/test-data/visual-tests/layout/multi-voice.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/page-layout-5barsperrow.png b/packages/alphatab/test-data/visual-tests/layout/page-layout-5barsperrow.png index 9637543c5..aea34f916 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/page-layout-5barsperrow.png and b/packages/alphatab/test-data/visual-tests/layout/page-layout-5barsperrow.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/page-layout-justify-last-row.png b/packages/alphatab/test-data/visual-tests/layout/page-layout-justify-last-row.png index 877aee4be..4e6195adf 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/page-layout-justify-last-row.png and b/packages/alphatab/test-data/visual-tests/layout/page-layout-justify-last-row.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/page-layout.png b/packages/alphatab/test-data/visual-tests/layout/page-layout.png index 960895914..b373a000c 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/page-layout.png and b/packages/alphatab/test-data/visual-tests/layout/page-layout.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-hide.png b/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-hide.png index 71681590e..51e8108ac 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-hide.png and b/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-hide.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-show.png b/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-show.png index 2d14ecc48..fe6c5715e 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-show.png and b/packages/alphatab/test-data/visual-tests/layout/single-staff-brackets-show.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/system-divider.png b/packages/alphatab/test-data/visual-tests/layout/system-divider.png index aca5bc071..9625d3121 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/system-divider.png and b/packages/alphatab/test-data/visual-tests/layout/system-divider.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/system-layout-tex.png b/packages/alphatab/test-data/visual-tests/layout/system-layout-tex.png index e6ce159e0..caf3d1112 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/system-layout-tex.png and b/packages/alphatab/test-data/visual-tests/layout/system-layout-tex.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/track-names-all-systems-multi.png b/packages/alphatab/test-data/visual-tests/layout/track-names-all-systems-multi.png index ab41b1fa0..1dca84e65 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/track-names-all-systems-multi.png and b/packages/alphatab/test-data/visual-tests/layout/track-names-all-systems-multi.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/track-names-first-system.png b/packages/alphatab/test-data/visual-tests/layout/track-names-first-system.png index 741d2c7a6..60e9fa97b 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/track-names-first-system.png and b/packages/alphatab/test-data/visual-tests/layout/track-names-first-system.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-all.png b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-all.png index d7636b6d0..ba9ed0614 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-all.png and b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-all.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-horizontal.png b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-horizontal.png index 5fb36e636..4c959e648 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-horizontal.png and b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-horizontal.png differ diff --git a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-short-name.png b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-short-name.png index 5eade17a4..dd736674d 100644 Binary files a/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-short-name.png and b/packages/alphatab/test-data/visual-tests/layout/track-names-full-name-short-name.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced-alphatex.png b/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced-alphatex.png index fce15b99f..26c7e2060 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced-alphatex.png and b/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced-alphatex.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced.png b/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced.png index d1691765a..86cf350f1 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced.png and b/packages/alphatab/test-data/visual-tests/music-notation/accidentals-advanced.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/beams-advanced.png b/packages/alphatab/test-data/visual-tests/music-notation/beams-advanced.png index 36b516ba6..d4b2c5a69 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/beams-advanced.png and b/packages/alphatab/test-data/visual-tests/music-notation/beams-advanced.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/brushes-ukulele.png b/packages/alphatab/test-data/visual-tests/music-notation/brushes-ukulele.png index a8b46ac7a..788944975 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/brushes-ukulele.png and b/packages/alphatab/test-data/visual-tests/music-notation/brushes-ukulele.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/clefs.png b/packages/alphatab/test-data/visual-tests/music-notation/clefs.png index 71185bf83..b0691407b 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/clefs.png and b/packages/alphatab/test-data/visual-tests/music-notation/clefs.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c3.png b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c3.png index e2e89e644..78728f37d 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c3.png and b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c3.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c4.png b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c4.png index be7b4b128..f90a8ba52 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c4.png and b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-c4.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-f4.png b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-f4.png index ce391b3d0..bba7c24eb 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-f4.png and b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-f4.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-g2.png b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-g2.png index 72e2539ec..e0a4f4335 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-g2.png and b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures-g2.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures.png b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures.png index 72e2539ec..e0a4f4335 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/key-signatures.png and b/packages/alphatab/test-data/visual-tests/music-notation/key-signatures.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/notes-rests-beams.png b/packages/alphatab/test-data/visual-tests/music-notation/notes-rests-beams.png index 3d47be520..c4cc93483 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/notes-rests-beams.png and b/packages/alphatab/test-data/visual-tests/music-notation/notes-rests-beams.png differ diff --git a/packages/alphatab/test-data/visual-tests/music-notation/rest-collisions.png b/packages/alphatab/test-data/visual-tests/music-notation/rest-collisions.png index 7e80b9baa..9e2ecf4ad 100644 Binary files a/packages/alphatab/test-data/visual-tests/music-notation/rest-collisions.png and b/packages/alphatab/test-data/visual-tests/music-notation/rest-collisions.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/full-default-large.png b/packages/alphatab/test-data/visual-tests/notation-legend/full-default-large.png index d8b421047..bbf9949be 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/full-default-large.png and b/packages/alphatab/test-data/visual-tests/notation-legend/full-default-large.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/full-default-small.png b/packages/alphatab/test-data/visual-tests/notation-legend/full-default-small.png index 0f2fb5288..57faec369 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/full-default-small.png and b/packages/alphatab/test-data/visual-tests/notation-legend/full-default-small.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/full-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/full-default.png index fb4c9f547..7c49b5266 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/full-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/full-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/full-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/full-songbook.png index cfe1f47cf..8ccb412b9 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/full-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/full-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/grace-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/grace-default.png index 02a526402..e0d7c516e 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/grace-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/grace-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/grace-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/grace-songbook.png index 2001b4833..43e6e3a02 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/grace-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/grace-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/hammer-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/hammer-default.png index 4dd89f7bb..3e1c779b9 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/hammer-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/hammer-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/hammer-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/hammer-songbook.png index 4dd89f7bb..3e1c779b9 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/hammer-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/hammer-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/mixed-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/mixed-default.png index ca1644730..09b1aa47d 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/mixed-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/mixed-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/mixed-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/mixed-songbook.png index 80504bcc6..f836570bf 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/mixed-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/mixed-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1300.png b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1300.png index 702be06e1..5a3b437b2 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1300.png and b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1300.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1500.png b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1500.png index e790d4194..6114a47d1 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1500.png and b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-1500.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-500.png b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-500.png index 09d34fed6..d7cc33386 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-500.png and b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-500.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-800.png b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-800.png index b8cee8afb..7d6c122e6 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-800.png and b/packages/alphatab/test-data/visual-tests/notation-legend/resize-sequence-800.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/slides-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/slides-default.png index 77366babd..8841ed27b 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/slides-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/slides-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/slides-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/slides-songbook.png index 77366babd..8841ed27b 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/slides-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/slides-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/smufl-petaluma-1300.png b/packages/alphatab/test-data/visual-tests/notation-legend/smufl-petaluma-1300.png index 11b2de995..ca949e06a 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/smufl-petaluma-1300.png and b/packages/alphatab/test-data/visual-tests/notation-legend/smufl-petaluma-1300.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-default.png b/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-default.png index 80e73d4d4..c1381ef61 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-default.png and b/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-default.png differ diff --git a/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-songbook.png b/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-songbook.png index 80e73d4d4..c1381ef61 100644 Binary files a/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-songbook.png and b/packages/alphatab/test-data/visual-tests/notation-legend/tap-riff-songbook.png differ diff --git a/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-ignores-displayscale.png b/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-ignores-displayscale.png new file mode 100644 index 000000000..629cfe4e2 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-ignores-displayscale.png differ diff --git a/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-prefix-system-start.png b/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-prefix-system-start.png new file mode 100644 index 000000000..c5d9d4796 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/prefix-overhead/page-automatic-prefix-system-start.png differ diff --git a/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-authored-scale-with-prefix.png b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-authored-scale-with-prefix.png new file mode 100644 index 000000000..4a3abc570 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-authored-scale-with-prefix.png differ diff --git a/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-midline-keysig-change.png b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-midline-keysig-change.png new file mode 100644 index 000000000..3c15332d7 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-midline-keysig-change.png differ diff --git a/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-system-start.png b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-system-start.png new file mode 100644 index 000000000..787440099 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/prefix-overhead/parchment-prefix-system-start.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300-2.png b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300-2.png index 5e4f2535c..fa4355ab3 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300-2.png and b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300-2.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300.png b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300.png index 4afdc155a..4699573ff 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300.png and b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-1300.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-800.png b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-800.png index bc788e0ca..19d4399ca 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-800.png and b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced-800.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced.png b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced.png index 4afdc155a..4699573ff 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced.png and b/packages/alphatab/test-data/visual-tests/special-notes/grace-notes-advanced.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-tracks/grand-staff.png b/packages/alphatab/test-data/visual-tests/special-tracks/grand-staff.png index 6c46a5cee..f202c8b3c 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-tracks/grand-staff.png and b/packages/alphatab/test-data/visual-tests/special-tracks/grand-staff.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-tracks/numbered.png b/packages/alphatab/test-data/visual-tests/special-tracks/numbered.png index 37dd291f0..554fbd63e 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-tracks/numbered.png and b/packages/alphatab/test-data/visual-tests/special-tracks/numbered.png differ diff --git a/packages/alphatab/test-data/visual-tests/special-tracks/slash.png b/packages/alphatab/test-data/visual-tests/special-tracks/slash.png index 3785d177a..7220afa89 100644 Binary files a/packages/alphatab/test-data/visual-tests/special-tracks/slash.png and b/packages/alphatab/test-data/visual-tests/special-tracks/slash.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-aligns-same-duration-notes.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-aligns-same-duration-notes.png new file mode 100644 index 000000000..b853426b0 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-aligns-same-duration-notes.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-horizontal-preserves-local.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-horizontal-preserves-local.png new file mode 100644 index 000000000..e781070d0 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-horizontal-preserves-local.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-multiple-short-arrivals.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-multiple-short-arrivals.png new file mode 100644 index 000000000..e61c4f461 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-multiple-short-arrivals.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-page-automatic.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-page-automatic.png new file mode 100644 index 000000000..3159ca11a Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-page-automatic.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-per-system-isolation.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-per-system-isolation.png new file mode 100644 index 000000000..92dbb7cf3 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-per-system-isolation.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-reconciles-on-resize.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-reconciles-on-resize.png new file mode 100644 index 000000000..496b23b7b Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-reconciles-on-resize.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-shorter-note-first.png b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-shorter-note-first.png new file mode 100644 index 000000000..7fc5a3594 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/shared-min-duration-shorter-note-first.png differ diff --git a/packages/alphatab/test-data/visual-tests/system-spacing/stretch-formula-duration-spacing.png b/packages/alphatab/test-data/visual-tests/system-spacing/stretch-formula-duration-spacing.png new file mode 100644 index 000000000..109e71804 Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/system-spacing/stretch-formula-duration-spacing.png differ diff --git a/packages/alphatab/test-data/visual-tests/systems-layout/bars-adjusted-model.png b/packages/alphatab/test-data/visual-tests/systems-layout/bars-adjusted-model.png index 4f9a97228..bdc5ca000 100644 Binary files a/packages/alphatab/test-data/visual-tests/systems-layout/bars-adjusted-model.png and b/packages/alphatab/test-data/visual-tests/systems-layout/bars-adjusted-model.png differ diff --git a/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-single-track.png b/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-single-track.png index a869f8c17..b2a837a48 100644 Binary files a/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-single-track.png and b/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-single-track.png differ diff --git a/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-two-tracks.png b/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-two-tracks.png index 735486de5..7f96dc6e6 100644 Binary files a/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-two-tracks.png and b/packages/alphatab/test-data/visual-tests/systems-layout/multi-track-two-tracks.png differ diff --git a/packages/alphatab/test-data/visual-tests/systems-layout/resized.png b/packages/alphatab/test-data/visual-tests/systems-layout/resized.png index 0098285fb..fc29e5df3 100644 Binary files a/packages/alphatab/test-data/visual-tests/systems-layout/resized.png and b/packages/alphatab/test-data/visual-tests/systems-layout/resized.png differ diff --git a/packages/alphatab/test/PrettyFormat.ts b/packages/alphatab/test/PrettyFormat.ts index 46eb805cb..f96c711b8 100644 --- a/packages/alphatab/test/PrettyFormat.ts +++ b/packages/alphatab/test/PrettyFormat.ts @@ -33,7 +33,7 @@ * @internal */ export class PrettyFormatConfig { - public escapeString: boolean = true; + public escapeString: boolean = false; public indent: string = ' '; public maxDepth: number = Number.POSITIVE_INFINITY; public maxWidth: number = Number.POSITIVE_INFINITY; @@ -166,7 +166,7 @@ export class PrettyFormat { return 'Uint32Array'; } if (Array.isArray(val)) { - return 'Array'; + return '_Array'; } if (val instanceof Set) { return 'Set'; @@ -200,7 +200,7 @@ export class PrettyFormat { return hitMaxDepth ? `[${arrayTypeName}]` : `${ - min ? '' : `${arrayTypeName} ` + min || arrayTypeName.startsWith('_') ? '' : `${arrayTypeName} ` }[${PrettyFormat.printIterableValues(TestPlatform.typedArrayAsUnknownIterable(val), config, indentation, depth, refs, PrettyFormat.printer)}]`; } diff --git a/packages/alphatab/test/audio/AlphaSynth.test.ts b/packages/alphatab/test/audio/AlphaSynth.test.ts index a75abe4de..82d2ad48d 100644 --- a/packages/alphatab/test/audio/AlphaSynth.test.ts +++ b/packages/alphatab/test/audio/AlphaSynth.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { AlphaSynthMidiFileHandler } from '@coderline/alphatab/midi/AlphaSynthMidiFileHandler'; @@ -17,7 +18,6 @@ import { AudioExportOptions } from '@coderline/alphatab/synth/IAudioExporter'; import { SynthConstants } from '@coderline/alphatab/synth/SynthConstants'; import { TinySoundFont } from '@coderline/alphatab/synth/synthesis/TinySoundFont'; import { VorbisFile } from '@coderline/alphatab/synth/vorbis/VorbisFile'; -import { assert, expect } from 'chai'; import { TestOutput } from 'test/audio/TestOutput'; import { TestPlatform } from 'test/TestPlatform'; @@ -69,12 +69,12 @@ describe('AlphaSynthTests', () => { synth.loadSoundFont(data, false); synth.loadMidiFile(midi); - expect(synth.isReadyForPlayback).to.be.true; - expect(synth.hasSamplesForProgram(24)).to.be.true; - expect(synth.hasSamplesForProgram(30)).to.be.true; - expect(synth.hasSamplesForProgram(1)).to.be.false; - expect(synth.hasSamplesForProgram(35)).to.be.false; - expect(synth.hasSamplesForPercussion(SynthConstants.MetronomeKey)).to.be.true; + expect(synth.isReadyForPlayback).toBe(true); + expect(synth.hasSamplesForProgram(24)).toBe(true); + expect(synth.hasSamplesForProgram(30)).toBe(true); + expect(synth.hasSamplesForProgram(1)).toBe(false); + expect(synth.hasSamplesForProgram(35)).toBe(false); + expect(synth.hasSamplesForPercussion(SynthConstants.MetronomeKey)).toBe(true); }); it('only-used-instruments-decoded-sf3', async () => { @@ -99,30 +99,30 @@ describe('AlphaSynthTests', () => { synth.loadSoundFont(data, false); synth.loadMidiFile(midi); - expect(synth.isReadyForPlayback).to.be.true; - expect(synth.hasSamplesForProgram(24)).to.be.true; - expect(synth.hasSamplesForProgram(30)).to.be.true; - expect(synth.hasSamplesForProgram(1)).to.be.false; - expect(synth.hasSamplesForProgram(35)).to.be.false; - expect(synth.hasSamplesForPercussion(SynthConstants.MetronomeKey)).to.be.true; + expect(synth.isReadyForPlayback).toBe(true); + expect(synth.hasSamplesForProgram(24)).toBe(true); + expect(synth.hasSamplesForProgram(30)).toBe(true); + expect(synth.hasSamplesForProgram(1)).toBe(false); + expect(synth.hasSamplesForProgram(35)).toBe(false); + expect(synth.hasSamplesForPercussion(SynthConstants.MetronomeKey)).toBe(true); }); async function testVorbisFile(name: string) { const data = await TestPlatform.loadFile(`test-data/audio/${name}.ogg`); const vorbis = new VorbisFile(ByteBuffer.fromBuffer(data)); - expect(vorbis.streams.length).to.equal(1); - expect(vorbis.streams[0].audioChannels).to.equal(2); - expect(vorbis.streams[0].audioSampleRate).to.equal(44100); - expect(vorbis.streams[0].samples.length).to.be.greaterThan(44100 * 0.05); + expect(vorbis.streams.length).toBe(1); + expect(vorbis.streams[0].audioChannels).toBe(2); + expect(vorbis.streams[0].audioSampleRate).toBe(44100); + expect(vorbis.streams[0].samples.length).toBeGreaterThan(44100 * 0.05); const generated = vorbis.streams[0].samples; const reference = new DataView((await TestPlatform.loadFile(`test-data/audio/${name}_alphaTab.pcm`)).buffer); try { - expect(generated.length).to.equal(reference.buffer.byteLength / 4); + expect(generated.length).toBe(reference.buffer.byteLength / 4); for (let i = 0; i < generated.length; i++) { - expect(generated[i]).to.equal(reference.getFloat32(i * 4, true), `Difference at index ${i}`); + expect(generated[i], `Difference at index ${i}`).toBe(reference.getFloat32(i * 4, true)); } } catch (e) { await TestPlatform.saveFile( @@ -138,7 +138,7 @@ describe('AlphaSynthTests', () => { await testVorbisFile('Short'); }); - it('ogg-vorbis-example', async () => { + it('ogg-vorbis-example', { timeout: 30000 }, async () => { await testVorbisFile('Example'); }); @@ -212,13 +212,13 @@ describe('AlphaSynthTests', () => { try { const reference = new DataView((await TestPlatform.loadFile(`test-data/audio/${fileName}.pcm`)).buffer); - expect(generated.length).to.equal(reference.buffer.byteLength / 4); + expect(generated.length).toBe(reference.buffer.byteLength / 4); for (let i = 0; i < generated.length; i++) { const expected = reference.getFloat32(i * 4, true); if (generated[i] !== expected) { // custom check, chai assertion has quite huge overhead if called that often - expect(generated[i]).to.equal(expected, `Difference at index ${i}`); + expect(generated[i], `Difference at index ${i}`).toBe(expected); } } } catch (e) { @@ -320,22 +320,22 @@ describe('AlphaSynthTests', () => { } playTo(0); - expect(synth.channelGetPresetBank(0)).to.equal(77); - expect(synth.channelGetPresetBank(1)).to.equal(77); - expect(synth.channelGetPresetBank(2)).to.equal(50); - expect(synth.channelGetPresetBank(3)).to.equal(50); + expect(synth.channelGetPresetBank(0)).toBe(77); + expect(synth.channelGetPresetBank(1)).toBe(77); + expect(synth.channelGetPresetBank(2)).toBe(50); + expect(synth.channelGetPresetBank(3)).toBe(50); playTo(3840); - expect(synth.channelGetPresetBank(0)).to.equal(1000); - expect(synth.channelGetPresetBank(1)).to.equal(1000); - expect(synth.channelGetPresetBank(2)).to.equal(50); - expect(synth.channelGetPresetBank(3)).to.equal(50); + expect(synth.channelGetPresetBank(0)).toBe(1000); + expect(synth.channelGetPresetBank(1)).toBe(1000); + expect(synth.channelGetPresetBank(2)).toBe(50); + expect(synth.channelGetPresetBank(3)).toBe(50); playTo(3840 * 2); - expect(synth.channelGetPresetBank(0)).to.equal(1000); - expect(synth.channelGetPresetBank(1)).to.equal(1000); - expect(synth.channelGetPresetBank(2)).to.equal(4000); - expect(synth.channelGetPresetBank(3)).to.equal(4000); + expect(synth.channelGetPresetBank(0)).toBe(1000); + expect(synth.channelGetPresetBank(1)).toBe(1000); + expect(synth.channelGetPresetBank(2)).toBe(4000); + expect(synth.channelGetPresetBank(3)).toBe(4000); }); async function testPlaythrough(midi: MidiFile) { @@ -355,7 +355,7 @@ describe('AlphaSynthTests', () => { while (!finished) { const now = Date.now(); if (now - start > 2000) { - assert.fail(`play did not complete after ${2000}ms`); + throw new Error(`play did not complete after ${2000}ms`); } testOutput.next(); } @@ -364,7 +364,7 @@ describe('AlphaSynthTests', () => { it('small-tempos', async () => { const score = ScoreLoader.loadScoreFromBytes(await TestPlatform.loadFile('test-data/audio/small-tempo.xml')); - expect(score.masterBars[0].tempoAutomations[0].value).to.equal(0.111); + expect(score.masterBars[0].tempoAutomations[0].value).toBe(0.111); const midi = new MidiFile(); const handler = new AlphaSynthMidiFileHandler(midi); @@ -372,8 +372,8 @@ describe('AlphaSynthTests', () => { generator.generate(); const tempoChange: MidiEvent[] = midi.events.filter(e => e instanceof TempoChangeEvent); - expect(tempoChange.length).to.equal(1); - expect((tempoChange[0] as TempoChangeEvent).beatsPerMinute).to.equal(0.111); + expect(tempoChange.length).toBe(1); + expect((tempoChange[0] as TempoChangeEvent).beatsPerMinute).toBe(0.111); await testPlaythrough(midi); }); @@ -381,7 +381,7 @@ describe('AlphaSynthTests', () => { it('zero-tempo', async () => { const score = ScoreLoader.loadScoreFromBytes(await TestPlatform.loadFile('test-data/audio/small-tempo.xml')); - expect(score.masterBars[0].tempoAutomations[0].value).to.equal(0.111); + expect(score.masterBars[0].tempoAutomations[0].value).toBe(0.111); score.masterBars[0].tempoAutomations[0].value = 0; const midi = new MidiFile(); @@ -390,8 +390,8 @@ describe('AlphaSynthTests', () => { generator.generate(); const tempoChange: MidiEvent[] = midi.events.filter(e => e instanceof TempoChangeEvent); - expect(tempoChange.length).to.equal(1); - expect((tempoChange[0] as TempoChangeEvent).beatsPerMinute).to.equal(0); + expect(tempoChange.length).toBe(1); + expect((tempoChange[0] as TempoChangeEvent).beatsPerMinute).toBe(0); await testPlaythrough(midi); }); diff --git a/packages/alphatab/test/audio/MidiFileGenerator.test.ts b/packages/alphatab/test/audio/MidiFileGenerator.test.ts index 3e24ad80d..9ade098c5 100644 --- a/packages/alphatab/test/audio/MidiFileGenerator.test.ts +++ b/packages/alphatab/test/audio/MidiFileGenerator.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { Gp3To5Importer } from '@coderline/alphatab/importer/Gp3To5Importer'; import { Gp7To8Importer } from '@coderline/alphatab/importer/Gp7To8Importer'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; @@ -31,10 +32,9 @@ import { AlphaSynth } from '@coderline/alphatab/synth/AlphaSynth'; import { AlphaSynthWrapper } from '@coderline/alphatab/synth/AlphaSynthWrapper'; import { PlaybackRange } from '@coderline/alphatab/synth/PlaybackRange'; import type { PositionChangedEventArgs } from '@coderline/alphatab/synth/PositionChangedEventArgs'; -import { expect } from 'chai'; import { FlatControlChangeEvent, - FlatMidiEvent, + type FlatMidiEvent, FlatMidiEventGenerator, FlatNoteBendEvent, FlatNoteEvent, @@ -59,13 +59,12 @@ describe('MidiFileGeneratorTest', () => { for (let i: number = 0; i < actualEvents.length; i++) { Logger.info('Test', `i[${i}] ${actualEvents[i]}`); if (i < expectedEvents.length) { - expect(expectedEvents[i].equals(actualEvents[i])).to.equal( - true, - `i[${i}] expected[${expectedEvents[i]}] !== actual[${actualEvents[i]}]` + expect(expectedEvents[i].equals(actualEvents[i]), `i[${i}] expected[${expectedEvents[i]}] !== actual[${actualEvents[i]}]`).toBe( + true ); } } - expect(actualEvents.length).to.equal(expectedEvents.length); + expect(actualEvents.length).toBe(expectedEvents.length); }; it('full-song', async () => { @@ -84,22 +83,22 @@ describe('MidiFileGeneratorTest', () => { midiFile.addEvent(new NoteOnEvent(0, 100, 0, 2, 0)); midiFile.addEvent(new NoteOnEvent(0, 50, 0, 3, 0)); midiFile.addEvent(new NoteOnEvent(0, 50, 0, 4, 0)); - expect((midiFile.tracks[0].events[0] as NoteOnEvent).noteKey).to.equal(0); - expect((midiFile.tracks[0].events[1] as NoteOnEvent).noteKey).to.equal(1); - expect((midiFile.tracks[0].events[2] as NoteOnEvent).noteKey).to.equal(3); - expect((midiFile.tracks[0].events[3] as NoteOnEvent).noteKey).to.equal(4); - expect((midiFile.tracks[0].events[4] as NoteOnEvent).noteKey).to.equal(2); + expect((midiFile.tracks[0].events[0] as NoteOnEvent).noteKey).toBe(0); + expect((midiFile.tracks[0].events[1] as NoteOnEvent).noteKey).toBe(1); + expect((midiFile.tracks[0].events[2] as NoteOnEvent).noteKey).toBe(3); + expect((midiFile.tracks[0].events[3] as NoteOnEvent).noteKey).toBe(4); + expect((midiFile.tracks[0].events[4] as NoteOnEvent).noteKey).toBe(2); }); it('bend', () => { const tex: string = ':4 15.6{b(0 4)} 15.6'; const score: Score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].staves[0].bars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).to.equal(1); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].staves[0].bars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toBe(1); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); generator.generate(); @@ -188,39 +187,39 @@ describe('MidiFileGeneratorTest', () => { // on beat let tick: number = 0; const ticks: number[] = []; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration).to.equal(3840); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration).toBe(3840); ticks.push(tick); tick += score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].playbackDuration).to.equal(120); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].playbackDuration).toBe(120); ticks.push(tick); tick += score.tracks[0].staves[0].bars[1].voices[0].beats[0].playbackDuration; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].playbackDuration).to.equal(3720); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].playbackDuration).toBe(3720); ticks.push(tick); tick += score.tracks[0].staves[0].bars[1].voices[0].beats[1].playbackDuration; // before beat - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].playbackDuration).to.equal(3720); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].playbackDuration).toBe(3720); ticks.push(tick); tick += score.tracks[0].staves[0].bars[2].voices[0].beats[0].playbackDuration; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].playbackDuration).to.equal(120); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].playbackDuration).toBe(120); ticks.push(tick); tick += score.tracks[0].staves[0].bars[3].voices[0].beats[0].playbackDuration; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].playbackDuration).to.equal(3840); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].playbackDuration).toBe(3840); ticks.push(tick); tick += score.tracks[0].staves[0].bars[3].voices[0].beats[1].playbackDuration; // bend - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].graceType).to.equal(GraceType.BendGrace); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].playbackDuration).to.equal(1920); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].graceType).toBe(GraceType.BendGrace); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].playbackDuration).toBe(1920); ticks.push(tick); tick += score.tracks[0].staves[0].bars[4].voices[0].beats[0].playbackDuration; - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].absolutePlaybackStart).to.equal(tick); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].playbackDuration).to.equal(1920); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].absolutePlaybackStart).toBe(tick); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].playbackDuration).toBe(1920); ticks.push(tick); tick += score.tracks[0].staves[0].bars[4].voices[0].beats[1].playbackDuration; const info: PlaybackInformation = score.tracks[0].playbackInfo; @@ -299,12 +298,12 @@ describe('MidiFileGeneratorTest', () => { it('bend-multi-point', () => { const tex: string = ':4 15.6{b(0 4 0)} 15.6'; const score: Score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].staves[0].bars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).to.equal(1); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].staves[0].bars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toBe(1); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); generator.generate(); @@ -637,8 +636,8 @@ describe('MidiFileGeneratorTest', () => { actualPlaybackDurations.push(beat.playbackDuration); beat = beat.nextBeat; } - expect(actualPlaybackStartTimes.join(',')).to.equal(expectedPlaybackStartTimes.join(',')); - expect(actualPlaybackDurations.join(',')).to.equal(expectedPlaybackDurations.join(',')); + expect(actualPlaybackStartTimes.join(',')).toBe(expectedPlaybackStartTimes.join(',')); + expect(actualPlaybackDurations.join(',')).toBe(expectedPlaybackDurations.join(',')); // prettier-ignore const expectedMidiStartTimes: number[] = [ 0, 640, 960, 1600, 1920, 2240, 2400, 2720, 2880, 3200, 3360, 3680, 3840, 4560, 4800, 5520, 5760, 6120, 6240, @@ -661,19 +660,19 @@ describe('MidiFileGeneratorTest', () => { actualMidiDurations.push(midiEvent.length); } } - expect(actualMidiStartTimes.join(',')).to.equal(expectedMidiStartTimes.join(',')); - expect(actualMidiDurations.join(',')).to.equal(expectedMidiDurations.join(',')); + expect(actualMidiStartTimes.join(',')).toBe(expectedMidiStartTimes.join(',')); + expect(actualMidiDurations.join(',')).toBe(expectedMidiDurations.join(',')); }); it('beat-multi-bend', () => { const tex: string = ':4 (15.6{b(0 4)} 14.6{b(0 8)}) 15.6'; const score: Score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].staves[0].bars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).to.equal(1); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].staves[0].bars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toBe(1); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); generator.generate(); @@ -792,8 +791,8 @@ describe('MidiFileGeneratorTest', () => { it('tied-vibrato', () => { const tex: string = '3.3{v}.4 -.3{v}.4'; const score: Score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieDestination).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieDestination).toBe(true); score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato = VibratoType.None; const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const settings = new Settings(); @@ -1190,7 +1189,7 @@ describe('MidiFileGeneratorTest', () => { it('full-bar-rest', () => { const tex: string = '\\ts 3 4 3.3.4 3.3.4 3.3.4 | r.1 | 3.3.4 3.3.4 3.3.4'; const score: Score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].isFullBarRest).to.be.true; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].isFullBarRest).toBe(true); const expectedNoteOnTimes: number[] = [ 0 * MidiUtils.QuarterTime, // note 1 @@ -1208,7 +1207,7 @@ describe('MidiFileGeneratorTest', () => { beat = beat.nextBeat; } - expect(noteOnTimes.join(',')).to.equal(expectedNoteOnTimes.join(',')); + expect(noteOnTimes.join(',')).toBe(expectedNoteOnTimes.join(',')); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); @@ -1221,7 +1220,7 @@ describe('MidiFileGeneratorTest', () => { noteOnTimes.push(evt.tick); } } - expect(noteOnTimes.join(',')).to.equal(expectedNoteOnTimes.join(',')); + expect(noteOnTimes.join(',')).toBe(expectedNoteOnTimes.join(',')); }); it('time-signature', () => { @@ -1241,21 +1240,21 @@ describe('MidiFileGeneratorTest', () => { } } - expect(timeSignature).to.be.ok; + expect(timeSignature).toBeTruthy(); const meta: TimeSignatureEvent = timeSignature as TimeSignatureEvent; const timeSignatureNumerator: number = meta.numerator; const timeSignatureDenominator: number = Math.pow(2, meta.denominatorIndex); - expect(timeSignatureNumerator).to.equal(3); - expect(timeSignatureDenominator).to.equal(4); + expect(timeSignatureNumerator).toBe(3); + expect(timeSignatureDenominator).toBe(4); }); it('first-bar-tempo', () => { const tex: string = '\\tempo 120 . \\tempo 60 3.3*4 | \\tempo 80 3.3*4'; const score: Score = parseTex(tex); - expect(score.tempo).to.be.equal(60); - expect(score.masterBars[0].tempoAutomations.length).to.equal(1); - expect(score.masterBars[0].tempoAutomations[0]!.value).to.be.equal(60); + expect(score.tempo).toBe(60); + expect(score.masterBars[0].tempoAutomations.length).toBe(1); + expect(score.masterBars[0].tempoAutomations[0]!.value).toBe(60); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); @@ -1268,8 +1267,8 @@ describe('MidiFileGeneratorTest', () => { } } - expect(tempoChanges.map(t => t.tick).join(',')).to.be.equal('0,3840'); - expect(tempoChanges.map(t => t.tempo).join(',')).to.be.equal('60,80'); + expect(tempoChanges.map(t => t.tick).join(',')).toBe('0,3840'); + expect(tempoChanges.map(t => t.tempo).join(',')).toBe('60,80'); }); it('has-valid-dynamics', () => { @@ -1280,12 +1279,12 @@ describe('MidiFileGeneratorTest', () => { const note1: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; const note2: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0]; // First note has already highest dynamics which is increased due to accentuation - expect(note1.dynamics).to.be.equal(DynamicValue.FFF); - expect(note1.accentuated).to.be.equal(AccentuationType.Normal); + expect(note1.dynamics).toBe(DynamicValue.FFF); + expect(note1.accentuated).toBe(AccentuationType.Normal); // Second note has lowest dynamics which is decreased due to ghost note - expect(note2.dynamics).to.be.equal(DynamicValue.PPP); - expect(note2.isGhost).to.be.true; + expect(note2.dynamics).toBe(DynamicValue.PPP); + expect(note2.isGhost).toBe(true); const expectedEvents: FlatMidiEvent[] = [ // channel init @@ -1352,10 +1351,10 @@ describe('MidiFileGeneratorTest', () => { millisDuration: number ) { const res = tickLookup.findBeat(new Set([0]), tick); - expect(res).to.be.ok; - expect(res!.beat.notes[0].fret).to.equal(fret); - expect(res!.tickDuration).to.equal(tickDuration); - expect(res!.duration).to.equal(millisDuration); + expect(res).toBeTruthy(); + expect(res!.beat.notes[0].fret).toBe(fret); + expect(res!.tickDuration).toBe(tickDuration); + expect(res!.duration).toBe(millisDuration); } it('beat-tempo-change', async () => { @@ -1386,8 +1385,8 @@ describe('MidiFileGeneratorTest', () => { } } - expect(tempoChanges.map(t => t.tick).join(',')).to.be.equal('0,1920,3840,6288,7680,9120,11520,12960,15120'); - expect(tempoChanges.map(t => t.tempo).join(',')).to.be.equal('120,60,100,120,121,120,121,120,121'); + expect(tempoChanges.map(t => t.tick).join(',')).toBe('0,1920,3840,6288,7680,9120,11520,12960,15120'); + expect(tempoChanges.map(t => t.tempo).join(',')).toBe('120,60,100,120,121,120,121,120,121'); const tickLookup = generator.tickLookup; @@ -1413,8 +1412,8 @@ describe('MidiFileGeneratorTest', () => { const tempoChangeTick = score.masterBars[1].start + score.masterBars[1].calculateDuration() * score.masterBars[1].tempoAutomations[1].ratioPosition; - expect(tempoChangeTick - beatStart).to.equal(528); - expect(beatEnd - tempoChangeTick).to.equal(432); + expect(tempoChangeTick - beatStart).toBe(528); + expect(beatEnd - tempoChangeTick).toBe(432); const firstPartMillis = MidiUtils.ticksToMillis(tempoChangeTick - beatStart, 100); const secondPartMillis = MidiUtils.ticksToMillis(beatEnd - tempoChangeTick, 120); @@ -1762,7 +1761,7 @@ describe('MidiFileGeneratorTest', () => { const expectedTimers: number[] = [0, 2000, 4000, 6000, 8000, 16000, 26000, 28000, -1]; - expect(actualTimers.join(',')).to.equal(expectedTimers.join(',')); + expect(actualTimers.join(',')).toBe(expectedTimers.join(',')); }); it('beat-timer-tempo-changes', () => { @@ -1785,8 +1784,8 @@ describe('MidiFileGeneratorTest', () => { // no timers at start let b: Beat | null = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; while (b !== null) { - expect(b.showTimer).to.be.true; - expect(b.timer).to.equal(null); + expect(b.showTimer).toBe(true); + expect(b.timer).toBe(null); b = b.nextBeat; } @@ -1809,7 +1808,7 @@ describe('MidiFileGeneratorTest', () => { 2000, 3000, 3500, 3625, 3875 ]; - expect(actualTimers.join(',')).to.equal(expectedTimers.join(',')); + expect(actualTimers.join(',')).toBe(expectedTimers.join(',')); }); it('transpose', () => { @@ -1823,36 +1822,36 @@ describe('MidiFileGeneratorTest', () => { \\transpose 12 r.1 | r.1 | C4.4 `); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); - expect(score.tracks[0].staves[0].transpositionPitch).to.equal(0); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); + expect(score.tracks[0].staves[0].transpositionPitch).toBe(0); - expect(score.tracks[1].staves[0].displayTranspositionPitch).to.equal(-12); - expect(score.tracks[1].staves[0].transpositionPitch).to.equal(0); + expect(score.tracks[1].staves[0].displayTranspositionPitch).toBe(-12); + expect(score.tracks[1].staves[0].transpositionPitch).toBe(0); - expect(score.tracks[2].staves[0].displayTranspositionPitch).to.equal(0); - expect(score.tracks[2].staves[0].transpositionPitch).to.equal(-12); + expect(score.tracks[2].staves[0].displayTranspositionPitch).toBe(0); + expect(score.tracks[2].staves[0].transpositionPitch).toBe(-12); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); const generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); generator.generate(); - expect(generator.transpositionPitches.has(0)).to.be.true; - expect(generator.transpositionPitches.get(0)!).to.equal(0); + expect(generator.transpositionPitches.has(0)).toBe(true); + expect(generator.transpositionPitches.get(0)!).toBeCloseTo(0); - expect(generator.transpositionPitches.has(1)).to.be.true; - expect(generator.transpositionPitches.get(1)!).to.equal(0); + expect(generator.transpositionPitches.has(1)).toBe(true); + expect(generator.transpositionPitches.get(1)!).toBeCloseTo(0); - expect(generator.transpositionPitches.has(2)).to.be.true; - expect(generator.transpositionPitches.get(2)!).to.equal(0); + expect(generator.transpositionPitches.has(2)).toBe(true); + expect(generator.transpositionPitches.get(2)!).toBeCloseTo(0); - expect(generator.transpositionPitches.has(3)).to.be.true; - expect(generator.transpositionPitches.get(3)!).to.equal(0); + expect(generator.transpositionPitches.has(3)).toBe(true); + expect(generator.transpositionPitches.get(3)!).toBeCloseTo(0); - expect(generator.transpositionPitches.has(4)).to.be.true; - expect(generator.transpositionPitches.get(4)!).to.equal(12); + expect(generator.transpositionPitches.has(4)).toBe(true); + expect(generator.transpositionPitches.get(4)!).toBe(12); - expect(generator.transpositionPitches.has(5)).to.be.true; - expect(generator.transpositionPitches.get(5)!).to.equal(12); + expect(generator.transpositionPitches.has(5)).toBe(true); + expect(generator.transpositionPitches.get(5)!).toBe(12); }); it('tickshift-flat', () => { @@ -1864,9 +1863,9 @@ describe('MidiFileGeneratorTest', () => { const generator = new MidiFileGenerator(score, null, handler); generator.generate(); - expect(handler.tickShift).to.equal(120); + expect(handler.tickShift).toBe(120); const firstNote = handler.midiEvents.find(e => e instanceof FlatNoteEvent) as FlatNoteEvent; - expect(firstNote.tick).to.equal(-120); + expect(firstNote.tick).toBe(-120); }); it('tickshift-synth', () => { @@ -1879,9 +1878,9 @@ describe('MidiFileGeneratorTest', () => { const generator = new MidiFileGenerator(score, null, handler); generator.generate(); - expect(handler.tickShift).to.equal(120); + expect(handler.tickShift).toBe(120); const firstNote = file.events.find(e => e instanceof NoteOnEvent) as NoteOnEvent; - expect(firstNote.tick).to.equal(0); + expect(firstNote.tick).toBe(0); }); it('synthwrapper-mapping', () => { @@ -1908,24 +1907,24 @@ describe('MidiFileGeneratorTest', () => { // check API -> Player mappings wrapper.tickPosition = -120; - expect(synth.tickPosition).to.equal(tickImprecision); + expect(synth.tickPosition).toBe(tickImprecision); wrapper.tickPosition = 0; - expect(synth.tickPosition).to.equal(120 + tickImprecision); + expect(synth.tickPosition).toBe(120 + tickImprecision); const range = new PlaybackRange(); range.startTick = 960; range.endTick = 1920; wrapper.playbackRange = range; - expect(synth.playbackRange!.startTick).to.equal(range.startTick + handler.tickShift); - expect(synth.playbackRange!.endTick).to.equal(range.endTick + handler.tickShift); + expect(synth.playbackRange!.startTick).toBe(range.startTick + handler.tickShift); + expect(synth.playbackRange!.endTick).toBe(range.endTick + handler.tickShift); // check API <- Player mappings wrapper.stop(); - expect(wrapper.tickPosition).to.equal(range.startTick + tickImprecision); - expect(wrapper.loadedMidiInfo!.endTick).to.equal(3840); - expect(wrapper.playbackRange!.startTick).to.equal(range.startTick); - expect(wrapper.playbackRange!.endTick).to.equal(range.endTick); + expect(wrapper.tickPosition).toBe(range.startTick + tickImprecision); + expect(wrapper.loadedMidiInfo!.endTick).toBe(3840); + expect(wrapper.playbackRange!.startTick).toBe(range.startTick); + expect(wrapper.playbackRange!.endTick).toBe(range.endTick); wrapper.playbackRange = null; let lastArgs: PositionChangedEventArgs | null = null; @@ -1933,8 +1932,8 @@ describe('MidiFileGeneratorTest', () => { lastArgs = e; }); wrapper.tickPosition = 0; - expect(lastArgs!.currentTick).to.equal(tickImprecision); - expect(synth.tickPosition).to.equal(handler.tickShift + tickImprecision); + expect(lastArgs!.currentTick).toBe(tickImprecision); + expect(synth.tickPosition).toBe(handler.tickShift + tickImprecision); }); describe('effect-note-durations', () => { diff --git a/packages/alphatab/test/audio/MidiPlaybackController.test.ts b/packages/alphatab/test/audio/MidiPlaybackController.test.ts index 4551f28ae..ad687a74b 100644 --- a/packages/alphatab/test/audio/MidiPlaybackController.test.ts +++ b/packages/alphatab/test/audio/MidiPlaybackController.test.ts @@ -1,10 +1,9 @@ +import { describe, expect, it } from 'vitest'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { Logger } from '@coderline/alphatab/Logger'; import { MidiPlaybackController } from '@coderline/alphatab/midi/MidiPlaybackController'; import type { Score } from '@coderline/alphatab/model/Score'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; -import { assert, expect } from 'chai'; - describe('MidiPlaybackControllerTest', () => { function testRepeat(score: Score, expectedIndexes: number[], maxBars: number): void { const controller: MidiPlaybackController = new MidiPlaybackController(score); @@ -16,7 +15,7 @@ describe('MidiPlaybackControllerTest', () => { controller.processCurrent(); if (controller.shouldPlay) { if (i > maxBars) { - assert.fail('Too many bars generated'); + throw new Error('Too many bars generated'); } Logger.debug('Test', `Checking index ${i}, expected[${expectedIndexes[i]}]`, i, expectedIndexes[i]); if (index !== expectedIndexes[i]) { @@ -28,14 +27,14 @@ describe('MidiPlaybackControllerTest', () => { controller.moveNext(); } if (errors.length > 0) { - assert.fail( + throw new Error( `Sequence errors: ${errors.join(', ')}, Expected: [${expectedIndexes.join( ', ' )}], Actual: [${actual.join(', ')}]` ); } - expect(i).to.equal(expectedIndexes.length); - expect(controller.finished).to.be.equal(true); + expect(i).toBe(expectedIndexes.length); + expect(controller.finished).toBe(true); } async function testGuitarProRepeat(file: string, expectedBars: number[], maxBars: number): Promise { diff --git a/packages/alphatab/test/audio/MidiTickLookup.test.ts b/packages/alphatab/test/audio/MidiTickLookup.test.ts index 8082468a0..4e151e9c6 100644 --- a/packages/alphatab/test/audio/MidiTickLookup.test.ts +++ b/packages/alphatab/test/audio/MidiTickLookup.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { Logger } from '@coderline/alphatab/Logger'; @@ -16,12 +17,18 @@ import { Beat } from '@coderline/alphatab/model/Beat'; import { Duration } from '@coderline/alphatab/model/Duration'; import { MasterBar } from '@coderline/alphatab/model/MasterBar'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; +import { Bar } from '@coderline/alphatab/model/Bar'; import { Note } from '@coderline/alphatab/model/Note'; -import type { Score } from '@coderline/alphatab/model/Score'; +import { Score } from '@coderline/alphatab/model/Score'; +import { Track } from '@coderline/alphatab/model/Track'; +import { Voice } from '@coderline/alphatab/model/Voice'; import { Settings } from '@coderline/alphatab/Settings'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; import { PlaybackRange } from '@coderline/alphatab/synth/PlaybackRange'; +import { FlatMidiEvent, FlatMidiEventGenerator, FlatNoteEvent } from 'test/audio/FlatMidiEventGenerator'; +import { AlphaTabApiBase } from '@coderline/alphatab/AlphaTabApiBase'; +import { TestUiFacade } from 'test/visualTests/TestUiFacade'; +import { PlayerMode } from '@coderline/alphatab/PlayerSettings'; describe('MidiTickLookupTest', () => { function buildLookup(score: Score, settings: Settings): MidiTickLookup { @@ -45,11 +52,11 @@ describe('MidiTickLookupTest', () => { const nb = new Beat(); lookup.addBeat(nb, 0, MidiUtils.QuarterTime); - expect(masterBarLookup.firstBeat).to.be.ok; - expect(masterBarLookup.firstBeat!.start).to.equal(0); - expect(masterBarLookup.firstBeat!.end).to.equal(MidiUtils.QuarterTime); - expect(masterBarLookup.firstBeat!.highlightedBeats.length).to.equal(1); - expect(masterBarLookup.firstBeat!.highlightedBeats[0].beat).to.equal(nb); + expect(masterBarLookup.firstBeat).toBeTruthy(); + expect(masterBarLookup.firstBeat!.start).toBe(0); + expect(masterBarLookup.firstBeat!.end).toBe(MidiUtils.QuarterTime); + expect(masterBarLookup.firstBeat!.highlightedBeats.length).toBe(1); + expect(masterBarLookup.firstBeat!.highlightedBeats[0].beat).toBe(nb); }); function prepareVariantTest(): MidiTickLookup { @@ -65,17 +72,17 @@ describe('MidiTickLookupTest', () => { lookup.addBeat(new Beat(), MidiUtils.QuarterTime * 0, MidiUtils.QuarterTime); lookup.addBeat(new Beat(), MidiUtils.QuarterTime * 1, MidiUtils.QuarterTime); - expect(masterBarLookup.firstBeat).to.be.ok; - expect(masterBarLookup.firstBeat!.start).to.equal(0); - expect(masterBarLookup.firstBeat!.end).to.equal(MidiUtils.QuarterTime); - expect(masterBarLookup.firstBeat!.highlightedBeats.length).to.equal(1); + expect(masterBarLookup.firstBeat).toBeTruthy(); + expect(masterBarLookup.firstBeat!.start).toBe(0); + expect(masterBarLookup.firstBeat!.end).toBe(MidiUtils.QuarterTime); + expect(masterBarLookup.firstBeat!.highlightedBeats.length).toBe(1); - expect(masterBarLookup.lastBeat).to.be.ok; - expect(masterBarLookup.lastBeat!.start).to.equal(MidiUtils.QuarterTime); - expect(masterBarLookup.lastBeat!.end).to.equal(2 * MidiUtils.QuarterTime); - expect(masterBarLookup.lastBeat!.highlightedBeats.length).to.equal(1); + expect(masterBarLookup.lastBeat).toBeTruthy(); + expect(masterBarLookup.lastBeat!.start).toBe(MidiUtils.QuarterTime); + expect(masterBarLookup.lastBeat!.end).toBe(2 * MidiUtils.QuarterTime); + expect(masterBarLookup.lastBeat!.highlightedBeats.length).toBe(1); - expect(masterBarLookup.firstBeat!.nextBeat).to.equal(masterBarLookup.lastBeat); + expect(masterBarLookup.firstBeat!.nextBeat).toBe(masterBarLookup.lastBeat); return lookup; } @@ -91,14 +98,14 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.lastBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(MidiUtils.QuarterTime * 2); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 3); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(MidiUtils.QuarterTime * 2); + expect(n1.end).toBe(MidiUtils.QuarterTime * 3); - expect(l1).to.equal(masterBar.firstBeat!); - expect(l1.nextBeat).to.equal(l2); - expect(l2.nextBeat).to.equal(n1); + expect(l1).toBe(masterBar.firstBeat!); + expect(l1.nextBeat).toBe(l2); + expect(l2.nextBeat).toBe(n1); }); it('variant-c', () => { @@ -112,15 +119,15 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.lastBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(MidiUtils.QuarterTime * 2); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 4); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(MidiUtils.QuarterTime * 2); + expect(n1.end).toBe(MidiUtils.QuarterTime * 4); - expect(l1).to.equal(masterBar.firstBeat!); - expect(l1.nextBeat).to.equal(l2); - expect(l2.nextBeat).to.equal(n1); - expect(n1).to.equal(masterBar.lastBeat!); + expect(l1).toBe(masterBar.firstBeat!); + expect(l1.nextBeat).toBe(l2); + expect(l2.nextBeat).toBe(n1); + expect(n1).toBe(masterBar.lastBeat!); }); it('variant-d', () => { @@ -134,15 +141,15 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime); - expect(n1.end).to.equal(0); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime); + expect(n1.end).toBe(0); - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-e', () => { @@ -156,15 +163,15 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime * 2); - expect(n1.end).to.equal(0); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime * 2); + expect(n1.end).toBe(0); - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-f', () => { @@ -179,26 +186,26 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; const n2 = n1.nextBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime * 0.5); - expect(n1.end).to.equal(0); - - expect(n2.highlightedBeats.length).to.equal(2); - expect(n2.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n2.highlightedBeats[1].beat).to.equal(nb); - expect(n2.start).to.equal(0); - expect(n2.end).to.equal(MidiUtils.QuarterTime * 0.5); - - expect(l1.highlightedBeats.length).to.equal(1); - expect(l1.start).to.equal(MidiUtils.QuarterTime * 0.5); - expect(l1.end).to.equal(MidiUtils.QuarterTime); - - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(n2); - expect(n2.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime * 0.5); + expect(n1.end).toBe(0); + + expect(n2.highlightedBeats.length).toBe(2); + expect(n2.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n2.highlightedBeats[1].beat).toBe(nb); + expect(n2.start).toBe(0); + expect(n2.end).toBe(MidiUtils.QuarterTime * 0.5); + + expect(l1.highlightedBeats.length).toBe(1); + expect(l1.start).toBe(MidiUtils.QuarterTime * 0.5); + expect(l1.end).toBe(MidiUtils.QuarterTime); + + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(n2); + expect(n2.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-g', () => { @@ -212,20 +219,20 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime); - expect(n1.end).to.equal(0); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime); + expect(n1.end).toBe(0); - expect(l1.highlightedBeats.length).to.equal(2); - expect(l1.highlightedBeats[1].beat).to.equal(nb); - expect(l1.start).to.equal(0); - expect(l1.end).to.equal(MidiUtils.QuarterTime); + expect(l1.highlightedBeats.length).toBe(2); + expect(l1.highlightedBeats[1].beat).toBe(nb); + expect(l1.start).toBe(0); + expect(l1.end).toBe(MidiUtils.QuarterTime); - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-h-variant-m', () => { @@ -240,31 +247,31 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; const n2 = l1.nextBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime); - expect(n1.end).to.equal(0); - - expect(l1.highlightedBeats.length).to.equal(2); - expect(l1.highlightedBeats[1].beat).to.equal(nb); - expect(l1.start).to.equal(0); - expect(l1.end).to.equal(MidiUtils.QuarterTime); - - expect(n2.highlightedBeats.length).to.equal(2); - expect(n2.highlightedBeats[0].beat).to.equal(l2.highlightedBeats[0].beat); - expect(n2.highlightedBeats[1].beat).to.equal(nb); - expect(n2.start).to.equal(MidiUtils.QuarterTime); - expect(n2.end).to.equal(MidiUtils.QuarterTime * 1.5); - - expect(l2.highlightedBeats.length).to.equal(1); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1.5); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); - - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(n2); - expect(n2.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime); + expect(n1.end).toBe(0); + + expect(l1.highlightedBeats.length).toBe(2); + expect(l1.highlightedBeats[1].beat).toBe(nb); + expect(l1.start).toBe(0); + expect(l1.end).toBe(MidiUtils.QuarterTime); + + expect(n2.highlightedBeats.length).toBe(2); + expect(n2.highlightedBeats[0].beat).toBe(l2.highlightedBeats[0].beat); + expect(n2.highlightedBeats[1].beat).toBe(nb); + expect(n2.start).toBe(MidiUtils.QuarterTime); + expect(n2.end).toBe(MidiUtils.QuarterTime * 1.5); + + expect(l2.highlightedBeats.length).toBe(1); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1.5); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); + + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(n2); + expect(n2.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-i', () => { @@ -278,24 +285,24 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n1.start).to.equal(0); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 0.5); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n1.start).toBe(0); + expect(n1.end).toBe(MidiUtils.QuarterTime * 0.5); - expect(l1.highlightedBeats.length).to.equal(2); - expect(l1.highlightedBeats[1].beat).to.equal(nb); - expect(l1.start).to.equal(MidiUtils.QuarterTime * 0.5); - expect(l1.end).to.equal(MidiUtils.QuarterTime); + expect(l1.highlightedBeats.length).toBe(2); + expect(l1.highlightedBeats[1].beat).toBe(nb); + expect(l1.start).toBe(MidiUtils.QuarterTime * 0.5); + expect(l1.end).toBe(MidiUtils.QuarterTime); - expect(l2.highlightedBeats.length).to.equal(1); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); + expect(l2.highlightedBeats.length).toBe(1); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-j', () => { @@ -310,30 +317,30 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; const n2 = n1.nextBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n1.start).to.equal(0); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 0.25); - - expect(n2.highlightedBeats.length).to.equal(2); - expect(n2.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n2.highlightedBeats[1].beat).to.equal(nb); - expect(n2.start).to.equal(MidiUtils.QuarterTime * 0.25); - expect(n2.end).to.equal(MidiUtils.QuarterTime * 0.75); - - expect(l1.highlightedBeats.length).to.equal(1); - expect(l1.start).to.equal(MidiUtils.QuarterTime * 0.75); - expect(l1.end).to.equal(MidiUtils.QuarterTime); - - expect(l2.highlightedBeats.length).to.equal(1); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); - - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(n2); - expect(n2.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n1.start).toBe(0); + expect(n1.end).toBe(MidiUtils.QuarterTime * 0.25); + + expect(n2.highlightedBeats.length).toBe(2); + expect(n2.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n2.highlightedBeats[1].beat).toBe(nb); + expect(n2.start).toBe(MidiUtils.QuarterTime * 0.25); + expect(n2.end).toBe(MidiUtils.QuarterTime * 0.75); + + expect(l1.highlightedBeats.length).toBe(1); + expect(l1.start).toBe(MidiUtils.QuarterTime * 0.75); + expect(l1.end).toBe(MidiUtils.QuarterTime); + + expect(l2.highlightedBeats.length).toBe(1); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); + + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(n2); + expect(n2.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-k-variant-m', () => { @@ -348,30 +355,30 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; const n2 = n1.nextBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n1.start).to.equal(0); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 0.25); - - expect(n2.highlightedBeats.length).to.equal(2); - expect(n2.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n2.highlightedBeats[1].beat).to.equal(nb); - expect(n2.start).to.equal(MidiUtils.QuarterTime * 0.25); - expect(n2.end).to.equal(MidiUtils.QuarterTime * 0.75); - - expect(l1.highlightedBeats.length).to.equal(1); - expect(l1.start).to.equal(MidiUtils.QuarterTime * 0.75); - expect(l1.end).to.equal(MidiUtils.QuarterTime); - - expect(l2.highlightedBeats.length).to.equal(1); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); - - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(n2); - expect(n2.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n1.start).toBe(0); + expect(n1.end).toBe(MidiUtils.QuarterTime * 0.25); + + expect(n2.highlightedBeats.length).toBe(2); + expect(n2.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n2.highlightedBeats[1].beat).toBe(nb); + expect(n2.start).toBe(MidiUtils.QuarterTime * 0.25); + expect(n2.end).toBe(MidiUtils.QuarterTime * 0.75); + + expect(l1.highlightedBeats.length).toBe(1); + expect(l1.start).toBe(MidiUtils.QuarterTime * 0.75); + expect(l1.end).toBe(MidiUtils.QuarterTime); + + expect(l2.highlightedBeats.length).toBe(1); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); + + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(n2); + expect(n2.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-l', () => { @@ -383,12 +390,12 @@ describe('MidiTickLookupTest', () => { const nb = new Beat(); lookup.addBeat(nb, l1.start, MidiUtils.QuarterTime); - expect(l1.highlightedBeats.length).to.equal(2); - expect(l1.highlightedBeats[1].beat).to.equal(nb); + expect(l1.highlightedBeats.length).toBe(2); + expect(l1.highlightedBeats[1].beat).toBe(nb); - expect(l1).to.equal(masterBar.firstBeat!); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(l1).toBe(masterBar.firstBeat!); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-m', () => { @@ -402,24 +409,24 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; - expect(n1.highlightedBeats.length).to.equal(2); - expect(n1.highlightedBeats[0].beat).to.equal(l1.highlightedBeats[0].beat); - expect(n1.highlightedBeats[1].beat).to.equal(nb); - expect(n1.start).to.equal(0); - expect(n1.end).to.equal(MidiUtils.QuarterTime * 0.5); + expect(n1.highlightedBeats.length).toBe(2); + expect(n1.highlightedBeats[0].beat).toBe(l1.highlightedBeats[0].beat); + expect(n1.highlightedBeats[1].beat).toBe(nb); + expect(n1.start).toBe(0); + expect(n1.end).toBe(MidiUtils.QuarterTime * 0.5); - expect(l1.highlightedBeats.length).to.equal(1); - expect(l1.start).to.equal(MidiUtils.QuarterTime * 0.5); - expect(l1.end).to.equal(MidiUtils.QuarterTime); + expect(l1.highlightedBeats.length).toBe(1); + expect(l1.start).toBe(MidiUtils.QuarterTime * 0.5); + expect(l1.end).toBe(MidiUtils.QuarterTime); - expect(l2.highlightedBeats.length).to.equal(1); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); + expect(l2.highlightedBeats.length).toBe(1); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2).to.equal(masterBar.lastBeat!); + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2).toBe(masterBar.lastBeat!); }); it('variant-h-variant-n-variant-b', () => { @@ -434,31 +441,31 @@ describe('MidiTickLookupTest', () => { const n1 = masterBar.firstBeat!; const n2 = l2.nextBeat!; - expect(n1.highlightedBeats.length).to.equal(1); - expect(n1.highlightedBeats[0].beat).to.equal(nb); - expect(n1.start).to.equal(-MidiUtils.QuarterTime); - expect(n1.end).to.equal(0); - - expect(l1.highlightedBeats.length).to.equal(2); - expect(l1.highlightedBeats[1].beat).to.equal(nb); - expect(l1.start).to.equal(0); - expect(l1.end).to.equal(MidiUtils.QuarterTime); - - expect(l2.highlightedBeats.length).to.equal(2); - expect(l2.highlightedBeats[1].beat).to.equal(nb); - expect(l2.start).to.equal(MidiUtils.QuarterTime * 1); - expect(l2.end).to.equal(MidiUtils.QuarterTime * 2); - - expect(n2.highlightedBeats.length).to.equal(1); - expect(n2.highlightedBeats[0].beat).to.equal(nb); - expect(n2.start).to.equal(MidiUtils.QuarterTime * 2); - expect(n2.end).to.equal(MidiUtils.QuarterTime * 3); - - expect(n1).to.equal(masterBar.firstBeat!); - expect(n1.nextBeat).to.equal(l1); - expect(l1.nextBeat).to.equal(l2); - expect(l2.nextBeat).to.equal(n2); - expect(n2).to.equal(masterBar.lastBeat!); + expect(n1.highlightedBeats.length).toBe(1); + expect(n1.highlightedBeats[0].beat).toBe(nb); + expect(n1.start).toBe(-MidiUtils.QuarterTime); + expect(n1.end).toBe(0); + + expect(l1.highlightedBeats.length).toBe(2); + expect(l1.highlightedBeats[1].beat).toBe(nb); + expect(l1.start).toBe(0); + expect(l1.end).toBe(MidiUtils.QuarterTime); + + expect(l2.highlightedBeats.length).toBe(2); + expect(l2.highlightedBeats[1].beat).toBe(nb); + expect(l2.start).toBe(MidiUtils.QuarterTime * 1); + expect(l2.end).toBe(MidiUtils.QuarterTime * 2); + + expect(n2.highlightedBeats.length).toBe(1); + expect(n2.highlightedBeats[0].beat).toBe(nb); + expect(n2.start).toBe(MidiUtils.QuarterTime * 2); + expect(n2.end).toBe(MidiUtils.QuarterTime * 3); + + expect(n1).toBe(masterBar.firstBeat!); + expect(n1.nextBeat).toBe(l1); + expect(l1.nextBeat).toBe(l2); + expect(l2.nextBeat).toBe(n2); + expect(n2).toBe(masterBar.lastBeat!); }); function beatWithFret(fret: number) { @@ -519,30 +526,30 @@ describe('MidiTickLookupTest', () => { // validate first bar let current = lookup.masterBars[0].firstBeat!; - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('0,2'); - expect(current.start).to.equal(0); - expect(current.duration).to.equal(1920); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('0,2'); + expect(current.start).toBe(0); + expect(current.duration).toBe(1920); current = current.nextBeat!; - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('1,2'); - expect(current.start).to.equal(1920); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('1,2'); + expect(current.start).toBe(1920); // quarter note ends earlier due to grace note - expect(current.duration).to.equal(840); + expect(current.duration).toBe(840); current = current.nextBeat!; // on last slice we have the grace note but not the quarter note - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('2,3'); - expect(current.start).to.equal(2760); - expect(current.duration).to.equal(120); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('2,3'); + expect(current.start).toBe(2760); + expect(current.duration).toBe(120); // // validate second bar current = lookup.masterBars[1].firstBeat!; // no grace note, normal quarter note - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('4'); - expect(current.start).to.equal(0); - expect(current.duration).to.equal(960); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('4'); + expect(current.start).toBe(0); + expect(current.duration).toBe(960); }); it('grace-multivoice-with-overlap', () => { @@ -552,36 +559,36 @@ describe('MidiTickLookupTest', () => { // validate first bar let current = lookup.masterBars[0].firstBeat!; - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('0,2'); - expect(current.start).to.equal(0); - expect(current.duration).to.equal(1920); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('0,2'); + expect(current.start).toBe(0); + expect(current.duration).toBe(1920); current = current.nextBeat!; - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('1,2'); - expect(current.start).to.equal(1920); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('1,2'); + expect(current.start).toBe(1920); // quarter note ends earlier due to grace note - expect(current.duration).to.equal(840); + expect(current.duration).toBe(840); current = current.nextBeat!; // on last slice we have the grace note but not the quarter note - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('2,3'); - expect(current.start).to.equal(2760); - expect(current.duration).to.equal(120); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('2,3'); + expect(current.start).toBe(2760); + expect(current.duration).toBe(120); // // validate second bar current = lookup.masterBars[1].firstBeat!; // half the grace note - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('3'); - expect(current.start).to.equal(0); - expect(current.duration).to.equal(120); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('3'); + expect(current.start).toBe(0); + expect(current.duration).toBe(120); // no grace note, normal quarter note current = current.nextBeat!; - expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('4'); - expect(current.start).to.equal(120); - expect(current.duration).to.equal(840); + expect(current.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('4'); + expect(current.start).toBe(120); + expect(current.duration).toBe(840); }); it('cursor-snapping', async () => { @@ -596,32 +603,32 @@ describe('MidiTickLookupTest', () => { // with the quarter rest on the second voice as next beat const firstBeat = lookup.findBeat(tracks, 0, null); - expect(firstBeat!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[0].beats[0].id); - expect(firstBeat!.nextBeat!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); - expect(firstBeat!.beat.duration).to.equal(Duration.Whole); - expect(firstBeat!.nextBeat!.beat.duration).to.equal(Duration.Quarter); + expect(firstBeat!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[0].beats[0].id); + expect(firstBeat!.nextBeat!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); + expect(firstBeat!.beat.duration).toBe(Duration.Whole); + expect(firstBeat!.nextBeat!.beat.duration).toBe(Duration.Quarter); // Duration must only go to the next rest on the second voice despite the whole note - expect(firstBeat!.duration).to.equal(750); - expect(firstBeat!.beatLookup.duration).to.equal(960); + expect(firstBeat!.duration).toBe(750); + expect(firstBeat!.beatLookup.duration).toBe(960); // Still playing first beat const stillFirst = lookup.findBeat(tracks, 400, firstBeat); - expect(stillFirst!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[0].beats[0].id); - expect(stillFirst!.nextBeat!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); - expect(stillFirst!.beat.duration).to.equal(Duration.Whole); - expect(stillFirst!.nextBeat!.beat.duration).to.equal(Duration.Quarter); - expect(stillFirst!.duration).to.equal(750); - expect(stillFirst!.beatLookup.duration).to.equal(960); + expect(stillFirst!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[0].beats[0].id); + expect(stillFirst!.nextBeat!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); + expect(stillFirst!.beat.duration).toBe(Duration.Whole); + expect(stillFirst!.nextBeat!.beat.duration).toBe(Duration.Quarter); + expect(stillFirst!.duration).toBe(750); + expect(stillFirst!.beatLookup.duration).toBe(960); // Now we're past the second rest heading to the third const secondBeat = lookup.findBeat(tracks, 970 /* after first quarter */, stillFirst); - expect(secondBeat!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); - expect(secondBeat!.nextBeat!.beat.id).to.equal(score.tracks[0].staves[0].bars[0].voices[1].beats[2].id); - expect(secondBeat!.beat.duration).to.equal(Duration.Quarter); - expect(secondBeat!.nextBeat!.beat.duration).to.equal(Duration.Quarter); - expect(secondBeat!.duration).to.equal(750); - expect(secondBeat!.beatLookup.duration).to.equal(960); + expect(secondBeat!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[1].beats[1].id); + expect(secondBeat!.nextBeat!.beat.id).toBe(score.tracks[0].staves[0].bars[0].voices[1].beats[2].id); + expect(secondBeat!.beat.duration).toBe(Duration.Quarter); + expect(secondBeat!.nextBeat!.beat.duration).toBe(Duration.Quarter); + expect(secondBeat!.duration).toBe(750); + expect(secondBeat!.beatLookup.duration).toBe(960); }); it('before-beat-grace-later-bars', () => { @@ -636,24 +643,24 @@ describe('MidiTickLookupTest', () => { const bar2 = lookup.masterBars[1]; let current = bar2.firstBeat; - expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('2'); - expect(current!.start).to.equal(0); - expect(current!.duration).to.equal(960); + expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('2'); + expect(current!.start).toBe(0); + expect(current!.duration).toBe(960); current = current!.nextBeat; - expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('3'); - expect(current!.start).to.equal(960); - expect(current!.duration).to.equal(840); // 120 ticks stolen by grace beats + expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('3'); + expect(current!.start).toBe(960); + expect(current!.duration).toBe(840); // 120 ticks stolen by grace beats current = current!.nextBeat; - expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('4'); - expect(current!.start).to.equal(960 + 840); - expect(current!.duration).to.equal(60); + expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('4'); + expect(current!.start).toBe(960 + 840); + expect(current!.duration).toBe(60); current = current!.nextBeat; - expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).to.equal('5'); - expect(current!.start).to.equal(960 + 840 + 60); - expect(current!.duration).to.equal(60); + expect(current!.highlightedBeats.map(b => b.beat.notes[0].fret).join(',')).toBe('5'); + expect(current!.start).toBe(960 + 840 + 60); + expect(current!.duration).toBe(60); }); function lookupTest( @@ -707,7 +714,7 @@ describe('MidiTickLookupTest', () => { currentLookup = lookup.findBeat(tracks, ticks[i], currentLookup); Logger.info('Test', `Checking index ${i} with tick ${ticks[i]}`); - expect(currentLookup).to.be.ok; + expect(currentLookup).toBeTruthy(); actualIncrementalIds.push(idOfBeat(currentLookup!.beat)); actualIncrementalNextIds.push(idOfBeat(currentLookup!.nextBeat?.beat ?? null)); actualIncrementalTickDurations.push(currentLookup!.tickDuration); @@ -722,20 +729,20 @@ describe('MidiTickLookupTest', () => { } } - expect(actualIncrementalIds.join(',')).to.equal(currentBeatIds.join(','), 'currentBeatIds mismatch'); - expect(actualIncrementalNextIds.join(',')).to.equal(nextBeatIds.join(','), 'nextBeatIds mismatch'); - expect(actualIncrementalTickDurations.join(',')).to.equal(durations.join(','), 'durations mismatch'); + expect(actualIncrementalIds.join(','), 'currentBeatIds mismatch').toBe(currentBeatIds.join(',')); + expect(actualIncrementalNextIds.join(','), 'nextBeatIds mismatch').toBe(nextBeatIds.join(',')); + expect(actualIncrementalTickDurations.join(','), 'durations mismatch').toBe(durations.join(',')); if (expectedCursorModes) { - expect(expectedCursorModes.map(m => MidiTickLookupFindBeatResultCursorMode[m]).join(',')).to.equal( - actualCursorModes.map(m => MidiTickLookupFindBeatResultCursorMode[m]).join(','), + expect( + expectedCursorModes.map(m => MidiTickLookupFindBeatResultCursorMode[m]).join(','), 'cursorModes mismatch' - ); + ).toBe(actualCursorModes.map(m => MidiTickLookupFindBeatResultCursorMode[m]).join(',')); } if (!skipClean) { - expect(actualCleanIds.join(',')).to.equal(currentBeatIds.join(','), 'cleanIds mismatch'); - expect(actualCleanNextIds.join(',')).to.equal(nextBeatIds.join(','), 'cleanNextIds mismatch'); - expect(actualCleanTickDurations.join(',')).to.equal(durations.join(','), 'cleanTickDurations mismatch'); + expect(actualCleanIds.join(','), 'cleanIds mismatch').toBe(currentBeatIds.join(',')); + expect(actualCleanNextIds.join(','), 'cleanNextIds mismatch').toBe(nextBeatIds.join(',')); + expect(actualCleanTickDurations.join(','), 'cleanTickDurations mismatch').toBe(durations.join(',')); } } @@ -1263,6 +1270,118 @@ describe('MidiTickLookupTest', () => { ); }); + it('resolves empty beat in mixed-content voice', () => { + // Regression: in a voice that has both isEmpty and non-empty beats, the tick lookup + // must still resolve the empty beats so click-to-seek / cursor navigation can land on + // them. A previous filter in BeatTickLookup.highlightBeat excluded isEmpty beats from + // non-empty voices, which broke cursor navigation to e.g. recording-grid slots after + // the first note was recorded. + const score = new Score(); + const track = new Track(); + track.ensureStaveCount(1); + score.addTrack(track); + const staff = track.staves[0]; + + const masterBar = new MasterBar(); + score.addMasterBar(masterBar); + const bar = new Bar(); + staff.addBar(bar); + const voice = new Voice(); + bar.addVoice(voice); + + const emptyBeat = new Beat(); + emptyBeat.isEmpty = true; + emptyBeat.duration = Duration.Quarter; + voice.addBeat(emptyBeat); + + const noteBeat = new Beat(); + noteBeat.duration = Duration.Quarter; + voice.addBeat(noteBeat); + noteBeat.addNote(new Note()); + + const settings = new Settings(); + score.finish(settings); + + const lookup = buildLookup(score, settings); + + // with the filter removed, an isEmpty beat in a mixed voice should still be findable + expect(voice.isEmpty).toBe(false); + const result = lookup.findBeat(new Set([0]), 0); + expect(result).not.toBe(null); + expect(result!.beat).toBe(emptyBeat); + }); + + it('multi-beat empty voice produces non-overlapping slices', () => { + // Regression: _generateBeat previously overrode every beat's audioDuration to + // masterBarDuration whenever bar.isEmpty was true. That was right for the typical + // "single whole-bar rest" placeholder case, but in a multi-beat empty voice + // (e.g. a recording grid of isEmpty=true placeholder slots) it made every beat claim + // the full bar - the resulting overlapping slices accumulated prior beats into each + // subsequent slice's highlightedBeats, which broke cursor/highlighting logic. + const score = new Score(); + const track = new Track(); + track.ensureStaveCount(1); + score.addTrack(track); + const staff = track.staves[0]; + + const masterBar = new MasterBar(); + score.addMasterBar(masterBar); + const bar = new Bar(); + staff.addBar(bar); + const voice = new Voice(); + bar.addVoice(voice); + + const slotCount = 16; + for (let i = 0; i < slotCount; i++) { + const b = new Beat(); + b.duration = Duration.Sixteenth; + b.isEmpty = true; + voice.addBeat(b); + } + + const settings = new Settings(); + score.finish(settings); + expect(bar.isEmpty).toBe(true); + expect(voice.beats.length).toBe(slotCount); + + const lookup = buildLookup(score, settings); + const mbLookup = lookup.masterBars[0]; + + let slice = mbLookup.firstBeat; + let sliceIdx = 0; + while (slice) { + // every slice must contain exactly one beat - otherwise slices are overlapping + // and the multi-beat empty voice has regressed. + expect(slice.highlightedBeats.length, `slice ${sliceIdx} range [${slice.start},${slice.end})`).toBe(1); + expect(slice.highlightedBeats[0].beat).toBe(voice.beats[sliceIdx]); + slice = slice.nextBeat; + sliceIdx++; + } + expect(sliceIdx).toBe(slotCount); + + // single-beat empty bar still gets the full-bar override (existing behaviour + // preserved for the typical whole-bar-rest placeholder case). + const singleBeatScore = new Score(); + const t = new Track(); + t.ensureStaveCount(1); + singleBeatScore.addTrack(t); + const mb2 = new MasterBar(); + singleBeatScore.addMasterBar(mb2); + const bar2 = new Bar(); + t.staves[0].addBar(bar2); + const v2 = new Voice(); + bar2.addVoice(v2); + const placeholder = new Beat(); + placeholder.isEmpty = true; + placeholder.duration = Duration.Quarter; + v2.addBeat(placeholder); + singleBeatScore.finish(settings); + + const singleLookup = buildLookup(singleBeatScore, settings); + const singleMb = singleLookup.masterBars[0]; + expect(singleMb.firstBeat!.end - singleMb.firstBeat!.start).toBe(singleMb.end - singleMb.start); + }); + describe('playback-range', () => { it('full-bar', () => { lookupTest( @@ -1358,4 +1477,55 @@ describe('MidiTickLookupTest', () => { ); }); }); + + it('swing-click-lookup', async () => { + const settings = new Settings(); + settings.core.engine = 'svg'; + + const score = ScoreLoader.loadAlphaTex(`\\tf triplet8th C4.8 * 8`, settings); + const handler = new FlatMidiEventGenerator(); + const generator = new MidiFileGenerator(score, settings, handler); + generator.generate(); + + const noteEvents = handler.midiEvents.filter(e => e instanceof FlatNoteEvent); + expect(noteEvents.length).toBe(8); + + const beats = score.tracks[0].staves[0].bars[0].voices[0].beats; + + const facade = new TestUiFacade(); + facade.rootContainer.width = 1300; + + settings.player.playerMode = PlayerMode.EnabledSynthesizer; + const api = new AlphaTabApiBase(facade, settings); + + const promise = Promise.withResolvers(); + api.postRenderFinished.on(() => { + promise.resolve(score); + }); + api.error.on(e => promise.reject(e)); + api.renderScore(score, [0]); + + await promise.promise; + + for (let i = 0; i < beats.length; i++) { + const range = generator.tickLookup.getRelativeBeatPlaybackRange(beats[i]); + expect(range).not.toBeUndefined(); + + const noteStart = noteEvents[i].tick; + const noteEnd = noteEvents[i].tick + (noteEvents[i] as FlatNoteEvent).length; + + expect(range!.startTick).toBe(noteStart); + expect(range!.endTick).toBe(noteEnd); + + const playbackRangePadding = 50; // small offset to avoid overshoot, see applyPlaybackRangeFromHighlight + if (i < beats.length - 1) { + api.highlightPlaybackRange(beats[i], beats[i + 1]); + api.applyPlaybackRangeFromHighlight(); + + expect(api.playbackRange!.startTick).toBe(noteStart); + const nextNoteEnd = noteEvents[i + 1].tick + (noteEvents[i + 1] as FlatNoteEvent).length; + expect(api.playbackRange!.endTick).toBe(nextNoteEnd - playbackRangePadding); + } + } + }); }); diff --git a/packages/alphatab/test/audio/SyncPoint.test.ts b/packages/alphatab/test/audio/SyncPoint.test.ts index 22f657cb9..aadf2bd4c 100644 --- a/packages/alphatab/test/audio/SyncPoint.test.ts +++ b/packages/alphatab/test/audio/SyncPoint.test.ts @@ -1,9 +1,5 @@ -import { - type IEventEmitterOfT, - type IEventEmitter, - EventEmitterOfT, - EventEmitter -} from '@coderline/alphatab/EventEmitter'; +import { describe, expect, it } from 'vitest'; +import { type IEventEmitterOfT, type IEventEmitter, EventEmitterOfT, EventEmitter } from '@coderline/alphatab/EventEmitter'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { AlphaSynthMidiFileHandler } from '@coderline/alphatab/midi/AlphaSynthMidiFileHandler'; import { MidiFile } from '@coderline/alphatab/midi/MidiFile'; @@ -24,7 +20,6 @@ import type { Hydra } from '@coderline/alphatab/synth/soundfont/Hydra'; import type { SynthEvent } from '@coderline/alphatab/synth/synthesis/SynthEvent'; import { FlatMidiEventGenerator } from 'test/audio/FlatMidiEventGenerator'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; describe('SyncPointTests', () => { it('sync-point-update', async () => { @@ -68,24 +63,24 @@ describe('SyncPointTests', () => { sequencer.loadMidi(midi); sequencer.currentUpdateCurrentTempo(0); - expect(sequencer.currentTempo).to.equal(90); - expect(sequencer.modifiedTempo).to.equal(90); + expect(sequencer.currentTempo).toBe(90); + expect(sequencer.modifiedTempo).toBe(90); sequencer.currentUpdateCurrentTempo(1000); - expect(sequencer.currentTempo).to.equal(90); - expect(sequencer.modifiedTempo).to.equal(90); + expect(sequencer.currentTempo).toBe(90); + expect(sequencer.modifiedTempo).toBe(90); sequencer.currentUpdateCurrentTempo(2000); - expect(sequencer.currentTempo).to.equal(90); - expect(sequencer.modifiedTempo).to.equal(90); + expect(sequencer.currentTempo).toBe(90); + expect(sequencer.modifiedTempo).toBe(90); sequencer.currentUpdateCurrentTempo(3000); - expect(sequencer.currentTempo).to.equal(120); - expect(sequencer.modifiedTempo).to.equal(120); + expect(sequencer.currentTempo).toBe(120); + expect(sequencer.modifiedTempo).toBe(120); sequencer.currentUpdateCurrentTempo(4000); - expect(sequencer.currentTempo).to.equal(120); - expect(sequencer.modifiedTempo).to.equal(120); + expect(sequencer.currentTempo).toBe(120); + expect(sequencer.modifiedTempo).toBe(120); }); async function syncPointTestScore() { @@ -117,15 +112,15 @@ describe('SyncPointTests', () => { const update = MidiFileGenerator.generateSyncPoints(score); - expect(generator.syncPoints.length).to.equal(update.length); + expect(generator.syncPoints.length).toBe(update.length); for (let i = 0; i < generator.syncPoints.length; i++) { - expect(update[i].masterBarIndex).to.equal(generator.syncPoints[i].masterBarIndex); - expect(update[i].masterBarOccurence).to.equal(generator.syncPoints[i].masterBarOccurence); - expect(update[i].syncBpm).to.equal(generator.syncPoints[i].syncBpm); - expect(update[i].syncTime).to.equal(generator.syncPoints[i].syncTime); - expect(update[i].synthBpm).to.equal(generator.syncPoints[i].synthBpm); - expect(update[i].synthTick).to.equal(generator.syncPoints[i].synthTick); - expect(update[i].synthTime).to.equal(generator.syncPoints[i].synthTime); + expect(update[i].masterBarIndex).toBe(generator.syncPoints[i].masterBarIndex); + expect(update[i].masterBarOccurence).toBe(generator.syncPoints[i].masterBarOccurence); + expect(update[i].syncBpm).toBe(generator.syncPoints[i].syncBpm); + expect(update[i].syncTime).toBe(generator.syncPoints[i].syncTime); + expect(update[i].synthBpm).toBe(generator.syncPoints[i].synthBpm); + expect(update[i].synthTick).toBe(generator.syncPoints[i].synthTick); + expect(update[i].synthTime).toBe(generator.syncPoints[i].synthTime); } }); diff --git a/packages/alphatab/test/audio/__snapshots__/AlphaSynth.test.ts.snap b/packages/alphatab/test/audio/__snapshots__/AlphaSynth.test.ts.snap index cc6bd6ab2..a0234b4a2 100644 --- a/packages/alphatab/test/audio/__snapshots__/AlphaSynth.test.ts.snap +++ b/packages/alphatab/test/audio/__snapshots__/AlphaSynth.test.ts.snap @@ -1,7 +1,7 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AlphaSynthTests midi-bank 1`] = ` -Array [ +exports[`AlphaSynthTests > midi-bank 1`] = ` +[ Map { "track" => 0, "tick" => 0, diff --git a/packages/alphatab/test/audio/__snapshots__/MidiFileGenerator.test.ts.snap b/packages/alphatab/test/audio/__snapshots__/MidiFileGenerator.test.ts.snap index 70aec95d3..aacb29bcc 100644 --- a/packages/alphatab/test/audio/__snapshots__/MidiFileGenerator.test.ts.snap +++ b/packages/alphatab/test/audio/__snapshots__/MidiFileGenerator.test.ts.snap @@ -1,7 +1,7 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`MidiFileGeneratorTest effect-note-durations dots tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > dots > tremolo-2 1`] = ` +[ "Note: C4 180", "Note: C4 180", "Note: C4 180", @@ -17,8 +17,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations dots tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > dots > tremolo-3 1`] = ` +[ "Note: C4 90", "Note: C4 90", "Note: C4 90", @@ -46,8 +46,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations dots trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > dots > trill 1`] = ` +[ "Note: C4 120", "Note: C5 120", "Note: C4 120", @@ -69,8 +69,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-before-beat tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-before-beat > tremolo-2 1`] = ` +[ "Note: C4 120", "Note: C4 90", "Note: C4 90", @@ -94,8 +94,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-before-beat tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-before-beat > tremolo-3 1`] = ` +[ "Note: C4 120", "Note: C4 45", "Note: C4 45", @@ -135,8 +135,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-before-beat trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-before-beat > trill 1`] = ` +[ "Note: C4 120", "Note: C4 120", "Note: C5 120", @@ -157,8 +157,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-on-beat tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-on-beat > tremolo-2 1`] = ` +[ "Note: C3 120", "Note: C4 90", "Note: C4 90", @@ -182,8 +182,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-on-beat tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-on-beat > tremolo-3 1`] = ` +[ "Note: C3 120", "Note: C4 45", "Note: C4 45", @@ -223,8 +223,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations grace-notes-on-beat trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > grace-notes-on-beat > trill 1`] = ` +[ "Note: C3 120", "Note: C4 120", "Note: C5 120", @@ -244,8 +244,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain dot 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > dot 1`] = ` +[ "Note: C4 720", "Note: D4 720", "Note: E4 720", @@ -253,8 +253,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > tremolo-2 1`] = ` +[ "Note: C4 120", "Note: C4 120", "Note: C4 120", @@ -274,8 +274,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > tremolo-3 1`] = ` +[ "Note: C4 60", "Note: C4 60", "Note: C4 60", @@ -311,8 +311,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > trill 1`] = ` +[ "Note: C4 120", "Note: C5 120", "Note: C4 120", @@ -332,8 +332,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain tripletfeel 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > tripletfeel 1`] = ` +[ "Note: C4 640", "Note: D4 320", "Note: E4 640", @@ -341,8 +341,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations plain tuplet 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > plain > tuplet 1`] = ` +[ "Note: C4 320", "Note: D4 320", "Note: E4 320", @@ -350,8 +350,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations triplet-feel tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > triplet-feel > tremolo-2 1`] = ` +[ "Note: C4 160", "Note: C4 160", "Note: C4 160", @@ -371,8 +371,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations triplet-feel tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > triplet-feel > tremolo-3 1`] = ` +[ "Note: C4 80", "Note: C4 80", "Note: C4 80", @@ -408,8 +408,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations triplet-feel trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > triplet-feel > trill 1`] = ` +[ "Note: C4 120", "Note: C5 120", "Note: C4 120", @@ -431,8 +431,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations tuplet tremolo-2 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > tuplet > tremolo-2 1`] = ` +[ "Note: C4 80", "Note: C4 80", "Note: C4 80", @@ -448,8 +448,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations tuplet tremolo-3 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > tuplet > tremolo-3 1`] = ` +[ "Note: C4 40", "Note: C4 40", "Note: C4 40", @@ -477,8 +477,8 @@ Array [ ] `; -exports[`MidiFileGeneratorTest effect-note-durations tuplet trill 1`] = ` -Array [ +exports[`MidiFileGeneratorTest > effect-note-durations > tuplet > trill 1`] = ` +[ "Note: C4 120", "Note: C5 120", "Note: C4 80", diff --git a/packages/alphatab/test/audio/__snapshots__/SyncPoint.test.ts.snap b/packages/alphatab/test/audio/__snapshots__/SyncPoint.test.ts.snap index b5c35e9ed..99ad5e979 100644 --- a/packages/alphatab/test/audio/__snapshots__/SyncPoint.test.ts.snap +++ b/packages/alphatab/test/audio/__snapshots__/SyncPoint.test.ts.snap @@ -1,7 +1,7 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`SyncPointTests modified-tempo-lookup 1`] = ` -Array [ +exports[`SyncPointTests > modified-tempo-lookup 1`] = ` +[ "0 => 120", "4000 => 240", "5000 => 240", @@ -14,8 +14,8 @@ Array [ ] `; -exports[`SyncPointTests playback-fast-backing-track 1`] = ` -Array [ +exports[`SyncPointTests > playback-fast-backing-track 1`] = ` +[ "0,120,240", "500,120,240", "1000,120,240", @@ -62,8 +62,8 @@ Array [ ] `; -exports[`SyncPointTests playback-fast-external-media 1`] = ` -Array [ +exports[`SyncPointTests > playback-fast-external-media 1`] = ` +[ "0,120,240", "500,120,240", "1000,120,240", @@ -110,8 +110,8 @@ Array [ ] `; -exports[`SyncPointTests playback-normal-backing-track 1`] = ` -Array [ +exports[`SyncPointTests > playback-normal-backing-track 1`] = ` +[ "0,120,120", "500,120,120", "1000,120,120", @@ -200,8 +200,8 @@ Array [ ] `; -exports[`SyncPointTests playback-normal-external-media 1`] = ` -Array [ +exports[`SyncPointTests > playback-normal-external-media 1`] = ` +[ "0,120,120", "500,120,120", "1000,120,120", @@ -290,8 +290,8 @@ Array [ ] `; -exports[`SyncPointTests seek-fast-backing-track 1`] = ` -Array [ +exports[`SyncPointTests > seek-fast-backing-track 1`] = ` +[ "2000,120,240", "5000,60,240", "13000,80,240", @@ -302,8 +302,8 @@ Array [ ] `; -exports[`SyncPointTests seek-fast-backing-track 2`] = ` -Array [ +exports[`SyncPointTests > seek-fast-backing-track 2`] = ` +[ 0, 0, 0.5208333333333334, @@ -315,8 +315,8 @@ Array [ ] `; -exports[`SyncPointTests seek-fast-external-media 1`] = ` -Array [ +exports[`SyncPointTests > seek-fast-external-media 1`] = ` +[ "2000,120,240", "5000,60,240", "13000,80,240", @@ -327,8 +327,8 @@ Array [ ] `; -exports[`SyncPointTests seek-fast-external-media 2`] = ` -Array [ +exports[`SyncPointTests > seek-fast-external-media 2`] = ` +[ 0, 0, 0.5208333333333334, @@ -340,8 +340,8 @@ Array [ ] `; -exports[`SyncPointTests seek-normal-backing-track 1`] = ` -Array [ +exports[`SyncPointTests > seek-normal-backing-track 1`] = ` +[ "2000,120,120", "5000,120,120", "13000,60,120", @@ -352,8 +352,8 @@ Array [ ] `; -exports[`SyncPointTests seek-normal-backing-track 2`] = ` -Array [ +exports[`SyncPointTests > seek-normal-backing-track 2`] = ` +[ 0, 0, 0.5208333333333334, @@ -364,8 +364,8 @@ Array [ ] `; -exports[`SyncPointTests seek-normal-external-media 1`] = ` -Array [ +exports[`SyncPointTests > seek-normal-external-media 1`] = ` +[ "2000,120,120", "5000,120,120", "13000,60,120", @@ -376,8 +376,8 @@ Array [ ] `; -exports[`SyncPointTests seek-normal-external-media 2`] = ` -Array [ +exports[`SyncPointTests > seek-normal-external-media 2`] = ` +[ 0, 0, 0.5208333333333334, @@ -388,8 +388,8 @@ Array [ ] `; -exports[`SyncPointTests sync-point-generation 1`] = ` -Array [ +exports[`SyncPointTests > sync-point-generation 1`] = ` +[ "0,0,120,120,0,0", "2,0,120,240,4000,4000", "3,0,120,240,6000,5000", @@ -403,8 +403,8 @@ Array [ ] `; -exports[`SyncPointTests sync-point-generation-new 1`] = ` -Array [ +exports[`SyncPointTests > sync-point-generation-new 1`] = ` +[ "0,0,120,120,0,0", "4,0,60,60,8000,8000", "8,0,80,80,24000,24000", @@ -412,8 +412,8 @@ Array [ ] `; -exports[`SyncPointTests sync-point-update 1`] = ` -Array [ +exports[`SyncPointTests > sync-point-update 1`] = ` +[ "0,0,120,120,0,0", "2,0,120,240,4000,4000", "3,0,120,240,6000,5000", diff --git a/packages/alphatab/test/exporter/AlphaTexExporter.test.ts b/packages/alphatab/test/exporter/AlphaTexExporter.test.ts index 77f9c86ae..f0c6c17a3 100644 --- a/packages/alphatab/test/exporter/AlphaTexExporter.test.ts +++ b/packages/alphatab/test/exporter/AlphaTexExporter.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { AlphaTexExporter } from '@coderline/alphatab/exporter/AlphaTexExporter'; import { AlphaTexErrorWithDiagnostics } from '@coderline/alphatab/importer/AlphaTexImporter'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; @@ -5,8 +6,6 @@ import type { Score } from '@coderline/alphatab/model/Score'; import { Settings } from '@coderline/alphatab/Settings'; import { ComparisonHelpers } from 'test/model/ComparisonHelpers'; import { TestPlatform } from 'test/TestPlatform'; -import { assert } from 'chai'; - describe('AlphaTexExporterTest', () => { async function loadScore(name: string): Promise { const data = await TestPlatform.loadFile(`test-data/${name}`); @@ -50,7 +49,7 @@ describe('AlphaTexExporterTest', () => { } } - assert.fail( + throw new Error( `<${fileName}>${unwrapped.toString()}\n${errorLines.join('\n')}${error.stack}\n Tex:\n${exported}` ); } @@ -123,7 +122,7 @@ describe('AlphaTexExporterTest', () => { if (errors.length > 0) { await TestPlatform.saveFileAsString('test-data/exporter/notation-legend-formatted.atex.new', data); - assert.fail(errors.join('\n')); + throw new Error(errors.join('\n')); } else { await TestPlatform.deleteFile('test-data/exporter/notation-legend-formatted.atex.new'); } diff --git a/packages/alphatab/test/exporter/Gp7Exporter.test.ts b/packages/alphatab/test/exporter/Gp7Exporter.test.ts index 5c1f95695..271e705d3 100644 --- a/packages/alphatab/test/exporter/Gp7Exporter.test.ts +++ b/packages/alphatab/test/exporter/Gp7Exporter.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { Gp7Exporter } from '@coderline/alphatab/exporter/Gp7Exporter'; import { GpifInstrumentArticulation, @@ -16,7 +17,6 @@ import type { Score } from '@coderline/alphatab/model/Score'; import { Settings } from '@coderline/alphatab/Settings'; import { XmlDocument } from '@coderline/alphatab/xml/XmlDocument'; import { ZipReader } from '@coderline/alphatab/zip/ZipReader'; -import { assert, expect } from 'chai'; import { ComparisonHelpers } from 'test/model/ComparisonHelpers'; import { TestPlatform } from 'test/TestPlatform'; @@ -170,11 +170,11 @@ describe('Gp7ExporterTest', () => { ComparisonHelpers.expectJsonEqual(expectedJson, actualJson, '', ['accidentalmode']); - expect(actual.tracks[0].percussionArticulations.length).to.equal(2); - expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); - expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); - expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(0); - expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).to.equal(1); + expect(actual.tracks[0].percussionArticulations.length).toBe(2); + expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).toBe(0); + expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).toBe(1); + expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).toBe(0); + expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).toBe(1); }); it('gp7-lyrics-null', async () => { @@ -193,7 +193,8 @@ describe('Gp7ExporterTest', () => { it('percussion-articulations', async () => { const settings = new Settings(); const zip = new ZipReader( - ByteBuffer.fromBuffer(await TestPlatform.loadFile('test-data/exporter/articulations.gp')) + ByteBuffer.fromBuffer(await TestPlatform.loadFile('test-data/exporter/articulations.gp')), + settings.importer.maxDecodingBufferSize ).read(); const gpifData = zip.find(e => e.fileName === 'score.gpif')!.data; @@ -255,7 +256,7 @@ describe('Gp7ExporterTest', () => { const expected = await TestPlatform.loadFileAsString('test-data/exporter/articulations.source'); if (expected !== sourceCode) { await TestPlatform.saveFileAsString('test-data/exporter/articulations.source.new', sourceCode); - assert.fail('Articulations have changed, update the PercussionMapper and update the snapshot file'); + throw new Error('Articulations have changed, update the PercussionMapper and update the snapshot file'); } }); @@ -371,7 +372,8 @@ describe('Gp7ExporterTest', () => { it('sound-mapper', async () => { const settings = new Settings(); const zip = new ZipReader( - ByteBuffer.fromBuffer(await TestPlatform.loadFile('test-data/exporter/articulations.gp')) + ByteBuffer.fromBuffer(await TestPlatform.loadFile('test-data/exporter/articulations.gp')), + settings.importer.maxDecodingBufferSize ).read(); const gpifData = zip.find(e => e.fileName === 'score.gpif')!.data; @@ -411,12 +413,12 @@ describe('Gp7ExporterTest', () => { const expected = await TestPlatform.loadFileAsString('test-data/exporter/soundmapper.source'); if (expected !== sourceCode) { await TestPlatform.saveFileAsString('test-data/exporter/soundmapper.source.new', sourceCode); - assert.fail('RSE instrument set has, update the GpifSoundMapper and update the snapshot file'); + throw new Error('RSE instrument set has, update the GpifSoundMapper and update the snapshot file'); } }); function getInstrumentSet(gp: Uint8Array) { - const zip = new ZipReader(ByteBuffer.fromBuffer(gp)); + const zip = new ZipReader(ByteBuffer.fromBuffer(gp), new Settings().importer.maxDecodingBufferSize); const gpifData = zip.read().find(e => e.fileName === 'score.gpif')!.data; const xml = new XmlDocument(); xml.parse(IOHelper.toString(gpifData, '')); @@ -433,72 +435,62 @@ describe('Gp7ExporterTest', () => { const actualInstrumentSet = getInstrumentSet(exported); // order IS important for the elements and articulations. the InstrumentArticulation is index based. - expect(actualInstrumentSet.name).to.equal(expectedInstrumentSet.name); - expect(actualInstrumentSet.type).to.equal(expectedInstrumentSet.type); - expect(actualInstrumentSet.lineCount).to.equal(expectedInstrumentSet.lineCount); + expect(actualInstrumentSet.name).toBe(expectedInstrumentSet.name); + expect(actualInstrumentSet.type).toBe(expectedInstrumentSet.type); + expect(actualInstrumentSet.lineCount).toBe(expectedInstrumentSet.lineCount); const expectedElements = Array.from(expectedInstrumentSet.elements); const actualElements = Array.from(actualInstrumentSet.elements); for (let i = 0; i < expectedElements.length; i++) { const expectedElement = expectedElements[i]; - expect(actualElements.length).to.be.greaterThan( - i, - `Element ${i} (${expectedElement.name}) missing in actual file` + expect(actualElements.length, `Element ${i} (${expectedElement.name}) missing in actual file`).toBeGreaterThan( + i ); const actualElement = actualElements[i]; - expect(actualElement.name).to.equal(expectedElement.name); - expect(actualElement.type).to.equal(expectedElement.type); - expect(actualElement.soundbankName).to.equal(expectedElement.soundbankName); + expect(actualElement.name).toBe(expectedElement.name); + expect(actualElement.type).toBe(expectedElement.type); + expect(actualElement.soundbankName).toBe(expectedElement.soundbankName); for (let j = 0; j < expectedElement.articulations.length; j++) { const expectedArticulation = expectedElement.articulations[j]; - expect(actualElement.articulations.length).to.be.greaterThan( - j, - `Articulation ${i} missing in actual file` + expect(actualElement.articulations.length, `Articulation ${i} missing in actual file`).toBeGreaterThan( + j ); const actualArticulation = actualElement.articulations[j]; - expect(actualArticulation.name).to.equal(expectedArticulation.name); - expect(actualArticulation.staffLine).to.equal( - expectedArticulation.staffLine, - `Wrong staffline for articulation ${actualArticulation.name}` + expect(actualArticulation.name).toBe(expectedArticulation.name); + expect(actualArticulation.staffLine, `Wrong staffline for articulation ${actualArticulation.name}`).toBe( + expectedArticulation.staffLine ); - expect(actualArticulation.noteHeads.map(s => MusicFontSymbol[s]).join(' ')).to.equal( - expectedArticulation.noteHeads.map(s => MusicFontSymbol[s]).join(' '), - `Wrong noteHeads for articulation ${actualArticulation.name}` + expect(actualArticulation.noteHeads.map(s => MusicFontSymbol[s]).join(' '), `Wrong noteHeads for articulation ${actualArticulation.name}`).toBe( + expectedArticulation.noteHeads.map(s => MusicFontSymbol[s]).join(' ') ); - expect(MusicFontSymbol[actualArticulation.techniqueSymbol]).to.equal( - MusicFontSymbol[expectedArticulation.techniqueSymbol], - `Wrong techniqueSymbol for articulation ${actualArticulation.name}` + expect(MusicFontSymbol[actualArticulation.techniqueSymbol], `Wrong techniqueSymbol for articulation ${actualArticulation.name}`).toBe( + MusicFontSymbol[expectedArticulation.techniqueSymbol] ); - expect(TechniqueSymbolPlacement[actualArticulation.techniqueSymbolPlacement]).to.equal( - TechniqueSymbolPlacement[expectedArticulation.techniqueSymbolPlacement], - `Wrong techniqueSymbolPlacement for articulation ${actualArticulation.name}` + expect(TechniqueSymbolPlacement[actualArticulation.techniqueSymbolPlacement], `Wrong techniqueSymbolPlacement for articulation ${actualArticulation.name}`).toBe( + TechniqueSymbolPlacement[expectedArticulation.techniqueSymbolPlacement] ); - expect(actualArticulation.inputMidiNumbers.map(i => i.toString()).join(',')).to.equal( - expectedArticulation.inputMidiNumbers.map(i => i.toString()).join(','), - `Wrong inputMidiNumbers for articulation ${actualArticulation.name}` + expect(actualArticulation.inputMidiNumbers.map(i => i.toString()).join(','), `Wrong inputMidiNumbers for articulation ${actualArticulation.name}`).toBe( + expectedArticulation.inputMidiNumbers.map(i => i.toString()).join(',') ); - expect(actualArticulation.outputMidiNumber).to.equal( - expectedArticulation.outputMidiNumber, - `Wrong outputMidiNumber for articulation ${actualArticulation.name}` + expect(actualArticulation.outputMidiNumber, `Wrong outputMidiNumber for articulation ${actualArticulation.name}`).toBe( + expectedArticulation.outputMidiNumber ); - expect(actualArticulation.outputRSESound).to.equal( - expectedArticulation.outputRSESound, - `Wrong outputRSESound for articulation ${actualArticulation.name}` + expect(actualArticulation.outputRSESound, `Wrong outputRSESound for articulation ${actualArticulation.name}`).toBe( + expectedArticulation.outputRSESound ); } - expect(actualElement.articulations.length).to.equal( - expectedElement.articulations.length, - `articulation length mismatch on element ${expectedElement.name}` + expect(actualElement.articulations.length, `articulation length mismatch on element ${expectedElement.name}`).toBe( + expectedElement.articulations.length ); } - expect(actualInstrumentSet.elements.length).to.equal(expectedInstrumentSet.elements.length); + expect(actualInstrumentSet.elements.length).toBe(expectedInstrumentSet.elements.length); // await TestPlatform.saveFile('test-data/exporter/articulations.exported.gp', exported); }); diff --git a/packages/alphatab/test/global-hooks.ts b/packages/alphatab/test/global-hooks.ts index 1aaddd00f..06c8d9b10 100644 --- a/packages/alphatab/test/global-hooks.ts +++ b/packages/alphatab/test/global-hooks.ts @@ -1,22 +1,41 @@ /** @target web */ -import * as chai from 'chai'; -import { afterAll, beforeEachTest, initializeJestSnapshot } from './mocha.jest-snapshot'; import { TestPlatform } from 'test/TestPlatform'; +import { beforeEach, expect } from 'vitest'; +import { + AlphaTexAstNodePlugin, + AlphaTexDiagnosticPlugin, + MidiEventSerializerPlugin, + type PrettyFormatConfig, + type PrettyFormatPrinter, + ScoreSerializerPlugin +} from './PrettyFormat'; -export const mochaHooks = { - async beforeAll() { - chai.config.truncateThreshold = 0; // disable truncating - await initializeJestSnapshot(); - }, +const plugins = [ + ScoreSerializerPlugin.instance, + MidiEventSerializerPlugin.instance, + AlphaTexDiagnosticPlugin.instance, + AlphaTexAstNodePlugin.instance +]; - beforeEach: function (done) { - beforeEachTest(this.currentTest!); - TestPlatform.currentTestName = this.currentTest!.title; - done(); - }, +for (const plugin of plugins) { + expect.addSnapshotSerializer({ + test(val) { + return plugin.test(val); + }, + serialize(val, config, indentation, depth, refs, printer) { + return plugin.serialize( + val, + config as PrettyFormatConfig, + indentation, + depth, + refs, + printer as PrettyFormatPrinter + ); + } + }); +} - afterAll(done) { - afterAll(); - done(); - } -} satisfies Mocha.RootHookObject; +beforeEach(() => { + const fullName = expect.getState().currentTestName ?? ''; + TestPlatform.currentTestName = fullName.split(' > ').pop() ?? ''; +}); diff --git a/packages/alphatab/test/importer/AlphaTexImporter.test.ts b/packages/alphatab/test/importer/AlphaTexImporter.test.ts index 1b8308f87..39a76e468 100644 --- a/packages/alphatab/test/importer/AlphaTexImporter.test.ts +++ b/packages/alphatab/test/importer/AlphaTexImporter.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { AlphaTexExporter } from '@coderline/alphatab/exporter/AlphaTexExporter'; import { AlphaTexStaffNoteKind } from '@coderline/alphatab/importer/alphaTex/AlphaTexShared'; import { AlphaTexErrorWithDiagnostics, AlphaTexImporter } from '@coderline/alphatab/importer/AlphaTexImporter'; @@ -50,7 +51,6 @@ import { Settings } from '@coderline/alphatab/Settings'; import { StaveProfile } from '@coderline/alphatab/StaveProfile'; import { ComparisonHelpers } from 'test/model/ComparisonHelpers'; import { VisualTestHelper } from 'test/visualTests/VisualTestHelper'; -import { assert, expect } from 'chai'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { TremoloPickingEffectSerializer } from '@coderline/alphatab/generated/model/TremoloPickingEffectSerializer'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; @@ -64,7 +64,7 @@ describe('AlphaTexImporterTest', () => { importer.initFromString(tex, new Settings()); try { importer.readScore(); - assert.fail('Expected error on import'); + throw new Error('Expected error on import'); } catch { expect(importer.lexerDiagnostics.errors).toMatchSnapshot('lexer-diagnostics'); expect(importer.parserDiagnostics.errors).toMatchSnapshot('parser-diagnostics'); @@ -88,7 +88,7 @@ describe('AlphaTexImporterTest', () => { if (e instanceof UnsupportedFormatError) { throw new UnsupportedFormatError(withDiag.toString()); } else { - assert.fail(`${withDiag.toString()}`); + throw new Error(`${withDiag.toString()}`); } } throw e; @@ -119,53 +119,53 @@ describe('AlphaTexImporterTest', () => { 0.5.2 1.5.4 3.4.4 | 5.3.8 5.3.8 5.3.8 5.3.8 r.2`; const score = parseTex(tex); - expect(score.title).to.equal('Test'); - expect(score.words).to.equal('test'); - expect(score.music).to.equal('alphaTab'); - expect(score.copyright).to.equal('test'); - expect(score.tempo).to.equal(200); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].playbackInfo.program).to.equal(30); - expect(score.tracks[0].staves[0].capo).to.equal(2); - expect(score.tracks[0].staves[0].tuning.join(',')).to.equal('55,38,43,47,50,69'); - expect(score.masterBars.length).to.equal(2); + expect(score.title).toBe('Test'); + expect(score.words).toBe('test'); + expect(score.music).toBe('alphaTab'); + expect(score.copyright).toBe('test'); + expect(score.tempo).toBe(200); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].playbackInfo.program).toBe(30); + expect(score.tracks[0].staves[0].capo).toBe(2); + expect(score.tracks[0].staves[0].tuning.join(',')).toBe('55,38,43,47,50,69'); + expect(score.masterBars.length).toBe(2); // bars[0] - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).to.equal(Duration.Half); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).to.equal(Duration.Quarter); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).to.equal(Duration.Quarter); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).toBe(Duration.Half); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).toBe(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).toBe(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).toBe(3); // bars[1] - expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].notes.length).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].duration).to.equal(Duration.Half); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).to.equal(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].notes.length).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].duration).toBe(Duration.Half); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).toBe(true); }); it('tuning', () => { @@ -174,71 +174,71 @@ describe('AlphaTexImporterTest', () => { 0.5.1`; const score = parseTex(tex); - expect(score.tracks[0].staves[0].tuning.join(',')).to.equal(Tuning.getDefaultTuningFor(6)!.tunings.join(',')); + expect(score.tracks[0].staves[0].tuning.join(',')).toBe(Tuning.getDefaultTuningFor(6)!.tunings.join(',')); }); it('dead-notes1-issue79', () => { const tex: string = ':4 x.3'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).to.equal(true); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toBe(true); }); it('dead-notes2-issue79', () => { const tex: string = ':4 3.3{x}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).to.equal(true); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toBe(true); }); it('dead', () => { const tex: string = 'x.1 3.1{x}'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).to.equal(6); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).to.equal(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toBe(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toBe(6); }); it('trill-issue79', () => { const tex: string = ':4 3.3{tr 5 16}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).to.equal(Duration.Sixteenth); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).to.equal(5); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toBe(Duration.Sixteenth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toBe(5); }); it('tremolo-issue79', () => { const tex: string = ':4 3.3{tr 5 16}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).to.equal(Duration.Sixteenth); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).to.equal(5); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toBe(Duration.Sixteenth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toBe(5); }); it('tremolo-picking-issue79', () => { const tex: string = ':4 3.3{tp 16}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isTremolo).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tremoloPicking!.marks).to.equal(2); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tremoloPicking!.marks).toBe(2); }); it('brushes-arpeggio', () => { @@ -248,67 +248,67 @@ describe('AlphaTexImporterTest', () => { (1.1 2.2 3.3 4.4).4{bd} (1.1 2.2 3.3 4.4).8{bu} (1.1 2.2 3.3 4.4).2{ad} (1.1 2.2 3.3 4.4).16{au} r `; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).to.equal(BrushType.BrushDown); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration).to.equal(960); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).to.equal(BrushType.BrushUp); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].playbackDuration).to.equal(480); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].brushType).to.equal(BrushType.ArpeggioDown); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].playbackDuration).to.equal(1920); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].brushType).to.equal(BrushType.ArpeggioUp); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].playbackDuration).to.equal(240); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].isRest).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].brushType).to.equal(BrushType.None); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].playbackDuration).to.equal(240); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].brushDuration).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].brushType).to.equal(BrushType.BrushDown); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].brushDuration).to.equal(120); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].brushType).to.equal(BrushType.BrushUp); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].brushDuration).to.equal(120); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].brushType).to.equal(BrushType.ArpeggioDown); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].brushDuration).to.equal(120); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].brushType).to.equal(BrushType.ArpeggioUp); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].brushDuration).to.equal(120); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).to.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].brushType).to.equal(BrushType.None); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].brushDuration).to.equal(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].brushType).to.equal(BrushType.BrushDown); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].brushType).to.equal(BrushType.BrushUp); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].brushDuration).to.equal(30); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].brushType).to.equal(BrushType.ArpeggioDown); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].brushDuration).to.equal(480); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].brushType).to.equal(BrushType.ArpeggioUp); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].brushDuration).to.equal(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].isRest).to.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].brushType).to.equal(BrushType.None); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].brushDuration).to.equal(0); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).toBe(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration).toBe(960); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).toBe(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].playbackDuration).toBe(480); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].brushType).toBe(BrushType.ArpeggioDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].playbackDuration).toBe(1920); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].brushType).toBe(BrushType.ArpeggioUp); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].playbackDuration).toBe(240); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].isRest).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].brushType).toBe(BrushType.None); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].playbackDuration).toBe(240); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].brushDuration).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].brushType).toBe(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].brushDuration).toBe(120); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].brushType).toBe(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].brushDuration).toBe(120); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].brushType).toBe(BrushType.ArpeggioDown); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].brushDuration).toBe(120); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].brushType).toBe(BrushType.ArpeggioUp); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].brushDuration).toBe(120); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].brushType).toBe(BrushType.None); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].brushDuration).toBe(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].brushType).toBe(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].brushType).toBe(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].brushDuration).toBe(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].brushType).toBe(BrushType.ArpeggioDown); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].brushDuration).toBe(480); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].brushType).toBe(BrushType.ArpeggioUp); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].brushDuration).toBe(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].isRest).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].brushType).toBe(BrushType.None); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[4].brushDuration).toBe(0); testExportRoundtrip(score); }); it('hamonics-issue79', () => { const tex: string = ':8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toBe( HarmonicType.Natural ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).to.equal(HarmonicType.Tap); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).to.equal(HarmonicType.Pinch); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).to.equal(HarmonicType.Semi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toBe(HarmonicType.Pinch); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toBe(HarmonicType.Semi); }); it('hamonics-rendering-text-issue79', async () => { @@ -332,51 +332,51 @@ describe('AlphaTexImporterTest', () => { new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Natural))).exec( svg ) - ).to.be.ok; + ).toBeTruthy(); expect( new RegExp( regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Artificial)) ).exec(svg) - ).to.be.ok; + ).toBeTruthy(); expect( new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Tap))).exec(svg) - ).to.be.ok; + ).toBeTruthy(); expect( new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Pinch))).exec(svg) - ).to.be.ok; + ).toBeTruthy(); expect( new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Semi))).exec(svg) - ).to.be.ok; + ).toBeTruthy(); }); it('grace-issue79', () => { const tex: string = ':8 3.3{gr} 3.3{gr ob}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).to.equal(GraceType.BeforeBeat); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].graceType).to.equal(GraceType.OnBeat); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).toBe(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].graceType).toBe(GraceType.OnBeat); testExportRoundtrip(score); }); it('left-hand-finger-single-note', () => { const tex: string = ':8 3.3{lf 1} 3.3{lf 2} 3.3{lf 3} 3.3{lf 4} 3.3{lf 5}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).toBe( Fingers.LittleFinger ); testExportRoundtrip(score); @@ -385,20 +385,20 @@ describe('AlphaTexImporterTest', () => { it('right-hand-finger-single-note', () => { const tex: string = ':8 3.3{rf 1} 3.3{rf 2} 3.3{rf 3} 3.3{rf 4} 3.3{rf 5}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].rightHandFinger).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].rightHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].rightHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].rightHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].rightHandFinger).toBe( Fingers.LittleFinger ); testExportRoundtrip(score); @@ -407,21 +407,21 @@ describe('AlphaTexImporterTest', () => { it('left-hand-finger-chord', () => { const tex: string = ':8 (3.1{lf 1} 3.2{lf 2} 3.3{lf 3} 3.4{lf 4} 3.5{lf 5})'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].leftHandFinger).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].leftHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].leftHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].leftHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].leftHandFinger).toBe( Fingers.LittleFinger ); testExportRoundtrip(score); @@ -430,21 +430,21 @@ describe('AlphaTexImporterTest', () => { it('right-hand-finger-chord', () => { const tex: string = ':8 (3.1{rf 1} 3.2{rf 2} 3.3{rf 3} 3.4{rf 4} 3.5{rf 5})'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].rightHandFinger).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].rightHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].rightHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].rightHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].rightHandFinger).toBe( Fingers.LittleFinger ); testExportRoundtrip(score); @@ -453,97 +453,97 @@ describe('AlphaTexImporterTest', () => { it('unstringed', () => { const tex: string = '\\instrument piano . c4 c#4 d4 d#4 | c4 db4 d4 eb4'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].realValue).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].realValue).to.equal(61); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].realValue).to.equal(62); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].realValue).to.equal(63); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].realValue).to.equal(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].realValue).to.equal(61); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].realValue).to.equal(62); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).to.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].realValue).to.equal(63); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].realValue).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].realValue).toBe(61); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].realValue).toBe(62); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].realValue).toBe(63); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].realValue).toBe(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].realValue).toBe(61); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].realValue).toBe(62); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].realValue).toBe(63); testExportRoundtrip(score); }); it('multi-staff-default-settings', () => { const tex: string = '1.1 | 1.1 | \\staff 2.1 | 2.1'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(2); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[1].showTablature).to.be.equal(true); // default settings used + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(2); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); // default settings used - expect(score.tracks[0].staves[1].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[1].bars.length).to.equal(2); + expect(score.tracks[0].staves[1].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[1].bars.length).toBe(2); testExportRoundtrip(score); }); it('multi-staff-default-settings-braces', () => { const tex: string = '1.1 | 1.1 | \\staff{} 2.1 | 2.1'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(2); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[1].showTablature).to.be.equal(true); // default settings used + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(2); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); // default settings used - expect(score.tracks[0].staves[1].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[1].bars.length).to.equal(2); + expect(score.tracks[0].staves[1].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[1].bars.length).toBe(2); testExportRoundtrip(score); }); it('single-staff-with-setting', () => { const tex: string = '\\staff{score} 1.1 | 1.1'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(false); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); testExportRoundtrip(score); }); it('single-staff-with-slash', () => { const tex: string = '\\staff{slash} 1.1 | 1.1'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].staves[0].showSlash).to.be.equal(true); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(false); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(false); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].staves[0].showSlash).toBe(true); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(false); + expect(score.tracks[0].staves[0].bars.length).toBe(2); testExportRoundtrip(score); }); it('single-staff-with-score-and-slash', () => { const tex: string = '\\staff{score slash} 1.1 | 1.1'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].staves[0].showSlash).to.be.equal(true); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(false); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].staves[0].showSlash).toBe(true); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); testExportRoundtrip(score); }); @@ -552,42 +552,42 @@ describe('AlphaTexImporterTest', () => { \\staff{tabs} \\capo 2 2.1 | 2.1 | \\staff{score tabs} \\tuning A1 D2 A2 D3 G3 B3 E4 3.1 | 3.1`; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(3); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(false); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[1].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[1].showStandardNotation).to.be.equal(false); - expect(score.tracks[0].staves[1].bars.length).to.equal(2); - expect(score.tracks[0].staves[1].capo).to.equal(2); - expect(score.tracks[0].staves[2].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[2].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[2].bars.length).to.equal(2); - expect(score.tracks[0].staves[2].tuning.length).to.equal(7); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(3); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); + expect(score.tracks[0].staves[1].showStandardNotation).toBe(false); + expect(score.tracks[0].staves[1].bars.length).toBe(2); + expect(score.tracks[0].staves[1].capo).toBe(2); + expect(score.tracks[0].staves[2].showTablature).toBe(true); + expect(score.tracks[0].staves[2].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[2].bars.length).toBe(2); + expect(score.tracks[0].staves[2].tuning.length).toBe(7); testExportRoundtrip(score); }); it('multi-track', () => { const tex: string = '\\track "First" 1.1 | 1.1 | \\track "Second" 2.2 | 2.2'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(2); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].name).to.equal('First'); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(0); - expect(score.tracks[0].playbackInfo.secondaryChannel).to.equal(1); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[1].staves.length).to.equal(1); - expect(score.tracks[1].name).to.equal('Second'); - expect(score.tracks[1].playbackInfo.primaryChannel).to.equal(2); - expect(score.tracks[1].playbackInfo.secondaryChannel).to.equal(3); - expect(score.tracks[1].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[1].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[1].staves[0].bars.length).to.equal(2); + expect(score.tracks.length).toBe(2); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].name).toBe('First'); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(0); + expect(score.tracks[0].playbackInfo.secondaryChannel).toBe(1); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[1].staves.length).toBe(1); + expect(score.tracks[1].name).toBe('Second'); + expect(score.tracks[1].playbackInfo.primaryChannel).toBe(2); + expect(score.tracks[1].playbackInfo.secondaryChannel).toBe(3); + expect(score.tracks[1].staves[0].showTablature).toBe(true); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[1].staves[0].bars.length).toBe(2); testExportRoundtrip(score); }); @@ -595,32 +595,32 @@ describe('AlphaTexImporterTest', () => { const tex: string = '\\track 1.1 | 1.1 | \\track "Only Long Name" 2.2 | 2.2 | \\track "Very Long Name" "shrt" 3.3 | 3.3 '; const score = parseTex(tex); - expect(score.tracks.length).to.equal(3); - expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].name).to.equal(''); - expect(score.tracks[0].shortName).to.equal(''); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(0); - expect(score.tracks[0].playbackInfo.secondaryChannel).to.equal(1); - expect(score.tracks[0].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[0].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[1].staves.length).to.equal(1); - expect(score.tracks[1].name).to.equal('Only Long Name'); - expect(score.tracks[1].shortName).to.equal('Only Long '); - expect(score.tracks[1].playbackInfo.primaryChannel).to.equal(2); - expect(score.tracks[1].playbackInfo.secondaryChannel).to.equal(3); - expect(score.tracks[1].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[1].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[1].staves[0].bars.length).to.equal(2); - expect(score.tracks[2].staves.length).to.equal(1); - expect(score.tracks[2].name).to.equal('Very Long Name'); - expect(score.tracks[2].shortName).to.equal('shrt'); - expect(score.tracks[2].playbackInfo.primaryChannel).to.equal(4); - expect(score.tracks[2].playbackInfo.secondaryChannel).to.equal(5); - expect(score.tracks[2].staves[0].showTablature).to.be.equal(true); - expect(score.tracks[2].staves[0].showStandardNotation).to.be.equal(true); - expect(score.tracks[2].staves[0].bars.length).to.equal(2); + expect(score.tracks.length).toBe(3); + expect(score.masterBars.length).toBe(2); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].name).toBe(''); + expect(score.tracks[0].shortName).toBe(''); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(0); + expect(score.tracks[0].playbackInfo.secondaryChannel).toBe(1); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[1].staves.length).toBe(1); + expect(score.tracks[1].name).toBe('Only Long Name'); + expect(score.tracks[1].shortName).toBe('Only Long '); + expect(score.tracks[1].playbackInfo.primaryChannel).toBe(2); + expect(score.tracks[1].playbackInfo.secondaryChannel).toBe(3); + expect(score.tracks[1].staves[0].showTablature).toBe(true); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[1].staves[0].bars.length).toBe(2); + expect(score.tracks[2].staves.length).toBe(1); + expect(score.tracks[2].name).toBe('Very Long Name'); + expect(score.tracks[2].shortName).toBe('shrt'); + expect(score.tracks[2].playbackInfo.primaryChannel).toBe(4); + expect(score.tracks[2].playbackInfo.secondaryChannel).toBe(5); + expect(score.tracks[2].staves[0].showTablature).toBe(true); + expect(score.tracks[2].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[2].staves[0].bars.length).toBe(2); testExportRoundtrip(score); }); @@ -640,62 +640,62 @@ describe('AlphaTexImporterTest', () => { 1.2 3.2 0.1 1.1 `; const score = parseTex(tex); - expect(score.tracks.length).to.equal(3); - expect(score.masterBars.length).to.equal(1); + expect(score.tracks.length).toBe(3); + expect(score.masterBars.length).toBe(1); { const track1: Track = score.tracks[0]; - expect(track1.name).to.equal('Piano'); - expect(track1.staves.length).to.equal(2); - expect(track1.playbackInfo.program).to.equal(0); - expect(track1.playbackInfo.primaryChannel).to.equal(0); - expect(track1.playbackInfo.secondaryChannel).to.equal(1); + expect(track1.name).toBe('Piano'); + expect(track1.staves.length).toBe(2); + expect(track1.playbackInfo.program).toBe(0); + expect(track1.playbackInfo.primaryChannel).toBe(0); + expect(track1.playbackInfo.secondaryChannel).toBe(1); { const staff1: Staff = track1.staves[0]; - expect(staff1.showTablature).to.be.equal(false); - expect(staff1.showStandardNotation).to.be.equal(true); - expect(staff1.tuning.length).to.equal(0); - expect(staff1.bars.length).to.equal(1); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(false); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toBe(0); + expect(staff1.bars.length).toBe(1); + expect(staff1.bars[0].clef).toBe(Clef.G2); } { const staff2: Staff = track1.staves[1]; - expect(staff2.showTablature).to.be.equal(false); - expect(staff2.showStandardNotation).to.be.equal(true); - expect(staff2.tuning.length).to.equal(0); - expect(staff2.bars.length).to.equal(1); - expect(staff2.bars[0].clef).to.equal(Clef.F4); + expect(staff2.showTablature).toBe(false); + expect(staff2.showStandardNotation).toBe(true); + expect(staff2.tuning.length).toBe(0); + expect(staff2.bars.length).toBe(1); + expect(staff2.bars[0].clef).toBe(Clef.F4); } } { const track2: Track = score.tracks[1]; - expect(track2.name).to.equal('Guitar'); - expect(track2.staves.length).to.equal(1); - expect(track2.playbackInfo.program).to.equal(25); - expect(track2.playbackInfo.primaryChannel).to.equal(2); - expect(track2.playbackInfo.secondaryChannel).to.equal(3); + expect(track2.name).toBe('Guitar'); + expect(track2.staves.length).toBe(1); + expect(track2.playbackInfo.program).toBe(25); + expect(track2.playbackInfo.primaryChannel).toBe(2); + expect(track2.playbackInfo.secondaryChannel).toBe(3); { const staff1: Staff = track2.staves[0]; - expect(staff1.showTablature).to.be.equal(true); - expect(staff1.showStandardNotation).to.be.equal(false); - expect(staff1.tuning.length).to.equal(6); - expect(staff1.bars.length).to.equal(1); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(false); + expect(staff1.tuning.length).toBe(6); + expect(staff1.bars.length).toBe(1); + expect(staff1.bars[0].clef).toBe(Clef.G2); } } { const track3: Track = score.tracks[2]; - expect(track3.name).to.equal('Second Guitar'); - expect(track3.staves.length).to.equal(1); - expect(track3.playbackInfo.program).to.equal(25); - expect(track3.playbackInfo.primaryChannel).to.equal(4); - expect(track3.playbackInfo.secondaryChannel).to.equal(5); + expect(track3.name).toBe('Second Guitar'); + expect(track3.staves.length).toBe(1); + expect(track3.playbackInfo.program).toBe(25); + expect(track3.playbackInfo.primaryChannel).toBe(4); + expect(track3.playbackInfo.secondaryChannel).toBe(5); { const staff1: Staff = track3.staves[0]; - expect(staff1.showTablature).to.be.equal(true); - expect(staff1.showStandardNotation).to.be.equal(true); - expect(staff1.tuning.length).to.equal(6); - expect(staff1.bars.length).to.equal(1); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toBe(6); + expect(staff1.bars.length).toBe(1); + expect(staff1.bars[0].clef).toBe(Clef.G2); } } testExportRoundtrip(score); @@ -718,74 +718,74 @@ describe('AlphaTexImporterTest', () => { 1.2 3.2 0.1 1.1 `; const score = parseTex(tex); - expect(score.tracks.length).to.equal(3); - expect(score.masterBars.length).to.equal(3); + expect(score.tracks.length).toBe(3); + expect(score.masterBars.length).toBe(3); { const track1: Track = score.tracks[0]; - expect(track1.name).to.equal('Piano'); - expect(track1.staves.length).to.equal(2); - expect(track1.playbackInfo.program).to.equal(0); - expect(track1.playbackInfo.primaryChannel).to.equal(0); - expect(track1.playbackInfo.secondaryChannel).to.equal(1); + expect(track1.name).toBe('Piano'); + expect(track1.staves.length).toBe(2); + expect(track1.playbackInfo.program).toBe(0); + expect(track1.playbackInfo.primaryChannel).toBe(0); + expect(track1.playbackInfo.secondaryChannel).toBe(1); { const staff1: Staff = track1.staves[0]; - expect(staff1.showTablature).to.be.equal(false); - expect(staff1.showStandardNotation).to.be.equal(true); - expect(staff1.tuning.length).to.equal(0); - expect(staff1.bars.length).to.equal(3); - expect(staff1.bars[0].isEmpty).to.be.equal(false); - expect(staff1.bars[1].isEmpty).to.be.equal(true); - expect(staff1.bars[2].isEmpty).to.be.equal(true); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(false); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toBe(0); + expect(staff1.bars.length).toBe(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(true); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toBe(Clef.G2); } { const staff2: Staff = track1.staves[1]; - expect(staff2.showTablature).to.be.equal(false); - expect(staff2.showStandardNotation).to.be.equal(true); - expect(staff2.tuning.length).to.equal(0); - expect(staff2.bars.length).to.equal(3); - expect(staff2.bars[0].isEmpty).to.be.equal(false); - expect(staff2.bars[1].isEmpty).to.be.equal(false); - expect(staff2.bars[2].isEmpty).to.be.equal(false); - expect(staff2.bars[0].clef).to.equal(Clef.F4); + expect(staff2.showTablature).toBe(false); + expect(staff2.showStandardNotation).toBe(true); + expect(staff2.tuning.length).toBe(0); + expect(staff2.bars.length).toBe(3); + expect(staff2.bars[0].isEmpty).toBe(false); + expect(staff2.bars[1].isEmpty).toBe(false); + expect(staff2.bars[2].isEmpty).toBe(false); + expect(staff2.bars[0].clef).toBe(Clef.F4); } } { const track2: Track = score.tracks[1]; - expect(track2.name).to.equal('Guitar'); - expect(track2.staves.length).to.equal(1); - expect(track2.playbackInfo.program).to.equal(25); - expect(track2.playbackInfo.primaryChannel).to.equal(2); - expect(track2.playbackInfo.secondaryChannel).to.equal(3); + expect(track2.name).toBe('Guitar'); + expect(track2.staves.length).toBe(1); + expect(track2.playbackInfo.program).toBe(25); + expect(track2.playbackInfo.primaryChannel).toBe(2); + expect(track2.playbackInfo.secondaryChannel).toBe(3); { const staff1: Staff = track2.staves[0]; - expect(staff1.showTablature).to.be.equal(true); - expect(staff1.showStandardNotation).to.be.equal(false); - expect(staff1.tuning.length).to.equal(6); - expect(staff1.bars.length).to.equal(3); - expect(staff1.bars[0].isEmpty).to.be.equal(false); - expect(staff1.bars[1].isEmpty).to.be.equal(false); - expect(staff1.bars[2].isEmpty).to.be.equal(true); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(false); + expect(staff1.tuning.length).toBe(6); + expect(staff1.bars.length).toBe(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(false); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toBe(Clef.G2); } } { const track3: Track = score.tracks[2]; - expect(track3.name).to.equal('Second Guitar'); - expect(track3.staves.length).to.equal(1); - expect(track3.playbackInfo.program).to.equal(25); - expect(track3.playbackInfo.primaryChannel).to.equal(4); - expect(track3.playbackInfo.secondaryChannel).to.equal(5); + expect(track3.name).toBe('Second Guitar'); + expect(track3.staves.length).toBe(1); + expect(track3.playbackInfo.program).toBe(25); + expect(track3.playbackInfo.primaryChannel).toBe(4); + expect(track3.playbackInfo.secondaryChannel).toBe(5); { const staff1: Staff = track3.staves[0]; - expect(staff1.showTablature).to.be.equal(true); - expect(staff1.showStandardNotation).to.be.equal(true); - expect(staff1.tuning.length).to.equal(6); - expect(staff1.bars.length).to.equal(3); - expect(staff1.bars[0].isEmpty).to.be.equal(false); - expect(staff1.bars[1].isEmpty).to.be.equal(true); - expect(staff1.bars[2].isEmpty).to.be.equal(true); - expect(staff1.bars[0].clef).to.equal(Clef.G2); + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toBe(6); + expect(staff1.bars.length).toBe(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(true); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toBe(Clef.G2); } } testExportRoundtrip(score); @@ -794,32 +794,32 @@ describe('AlphaTexImporterTest', () => { it('slides', () => { const tex: string = '3.3{sl} 4.3 | 3.3{ss} 4.3 | 3.3{sib} 3.3{sia} 3.3{sou} 3.3{sod} | 3.3{psd} 3.3{psu}'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.Legato ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideTarget!.id).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideTarget!.id).toBe( score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].id ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).to.equal(SlideOutType.Shift); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideTarget!.id).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).toBe(SlideOutType.Shift); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideTarget!.id).toBe( score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].id ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideInType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideInType).toBe( SlideInType.IntoFromBelow ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideInType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideInType).toBe( SlideInType.IntoFromAbove ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).to.equal(SlideOutType.OutUp); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).toBe(SlideOutType.OutUp); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).toBe( SlideOutType.OutDown ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).toBe( SlideOutType.PickSlideUp ); testExportRoundtrip(score); @@ -828,18 +828,18 @@ describe('AlphaTexImporterTest', () => { it('section', () => { const tex: string = '\\section Intro 1.1 | 1.1 | \\section "Chorus 01" 1.1 | \\section S Solo'; const score = parseTex(tex); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(4); - expect(score.masterBars[0].isSectionStart).to.be.equal(true); - expect(score.masterBars[0].section!.text).to.equal('Intro'); - expect(score.masterBars[0].section!.marker).to.equal(''); - expect(score.masterBars[1].isSectionStart).to.be.equal(false); - expect(score.masterBars[2].isSectionStart).to.be.equal(true); - expect(score.masterBars[2].section!.text).to.equal('Chorus 01'); - expect(score.masterBars[2].section!.marker).to.equal(''); - expect(score.masterBars[3].isSectionStart).to.be.equal(true); - expect(score.masterBars[3].section!.text).to.equal('Solo'); - expect(score.masterBars[3].section!.marker).to.equal('S'); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(4); + expect(score.masterBars[0].isSectionStart).toBe(true); + expect(score.masterBars[0].section!.text).toBe('Intro'); + expect(score.masterBars[0].section!.marker).toBe(''); + expect(score.masterBars[1].isSectionStart).toBe(false); + expect(score.masterBars[2].isSectionStart).toBe(true); + expect(score.masterBars[2].section!.text).toBe('Chorus 01'); + expect(score.masterBars[2].section!.marker).toBe(''); + expect(score.masterBars[3].isSectionStart).toBe(true); + expect(score.masterBars[3].section!.text).toBe('Solo'); + expect(score.masterBars[3].section!.marker).toBe('S'); testExportRoundtrip(score); }); @@ -870,8 +870,8 @@ describe('AlphaTexImporterTest', () => { ]; for (let i = 0; i < expected.length; i++) { - expect(bars[i].keySignature).to.equal(expected[i][0]); - expect(bars[i].keySignatureType).to.equal(expected[i][1]); + expect(bars[i].keySignature).toBe(expected[i][0]); + expect(bars[i].keySignatureType).toBe(expected[i][1]); } testExportRoundtrip(score); }); @@ -911,15 +911,15 @@ describe('AlphaTexImporterTest', () => { ]; for (let i = 0; i < expected.length; i++) { - expect(bars[i].keySignature).to.equal(expected[i][0]); - expect(bars[i].keySignatureType).to.equal(expected[i][1]); + expect(bars[i].keySignature).toBe(expected[i][0]); + expect(bars[i].keySignatureType).toBe(expected[i][1]); } bars = score.tracks[0].staves[1].bars; expected.reverse(); for (let i = 0; i < expected.length; i++) { - expect(bars[i].keySignature).to.equal(expected[i][0], `at ${i}`); - expect(bars[i].keySignatureType).to.equal(expected[i][1], `at ${i}`); + expect(bars[i].keySignature, `at ${i}`).toBe(expected[i][0]); + expect(bars[i].keySignatureType, `at ${i}`).toBe(expected[i][1]); } testExportRoundtrip(score); }); @@ -927,22 +927,22 @@ describe('AlphaTexImporterTest', () => { it('pop-slap-tap', () => { const tex: string = '3.3{p} 3.3{s} 3.3{tt} r'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].pop).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].slap).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].slap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); testExportRoundtrip(score); }); it('triplet-feel-numeric', () => { const tex: string = '\\tf 0 | \\tf 1 | \\tf 2 | \\tf 3 | \\tf 4 | \\tf 5 | \\tf 6'; const score = parseTex(tex); - expect(score.masterBars[0].tripletFeel).to.equal(TripletFeel.NoTripletFeel); - expect(score.masterBars[1].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[2].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[3].tripletFeel).to.equal(TripletFeel.Dotted16th); - expect(score.masterBars[4].tripletFeel).to.equal(TripletFeel.Dotted8th); - expect(score.masterBars[5].tripletFeel).to.equal(TripletFeel.Scottish16th); - expect(score.masterBars[6].tripletFeel).to.equal(TripletFeel.Scottish8th); + expect(score.masterBars[0].tripletFeel).toBe(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toBe(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toBe(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toBe(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toBe(TripletFeel.Scottish8th); testExportRoundtrip(score); }); @@ -950,41 +950,41 @@ describe('AlphaTexImporterTest', () => { const tex: string = '\\tf none | \\tf triplet-16th | \\tf triplet-8th | \\tf dotted-16th | \\tf dotted-8th | \\tf scottish-16th | \\tf scottish-8th'; const score = parseTex(tex); - expect(score.masterBars[0].tripletFeel).to.equal(TripletFeel.NoTripletFeel); - expect(score.masterBars[1].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[2].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[3].tripletFeel).to.equal(TripletFeel.Dotted16th); - expect(score.masterBars[4].tripletFeel).to.equal(TripletFeel.Dotted8th); - expect(score.masterBars[5].tripletFeel).to.equal(TripletFeel.Scottish16th); - expect(score.masterBars[6].tripletFeel).to.equal(TripletFeel.Scottish8th); + expect(score.masterBars[0].tripletFeel).toBe(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toBe(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toBe(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toBe(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toBe(TripletFeel.Scottish8th); testExportRoundtrip(score); }); it('triplet-feel-short-names', () => { const tex: string = '\\tf no | \\tf t16 | \\tf t8 | \\tf d16 | \\tf d8 | \\tf s16 | \\tf s8'; const score = parseTex(tex); - expect(score.masterBars[0].tripletFeel).to.equal(TripletFeel.NoTripletFeel); - expect(score.masterBars[1].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[2].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[3].tripletFeel).to.equal(TripletFeel.Dotted16th); - expect(score.masterBars[4].tripletFeel).to.equal(TripletFeel.Dotted8th); - expect(score.masterBars[5].tripletFeel).to.equal(TripletFeel.Scottish16th); - expect(score.masterBars[6].tripletFeel).to.equal(TripletFeel.Scottish8th); + expect(score.masterBars[0].tripletFeel).toBe(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toBe(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toBe(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toBe(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toBe(TripletFeel.Scottish8th); testExportRoundtrip(score); }); it('triplet-feel-multi-bar', () => { const tex: string = '\\tf t16 C4 | C4 | C4 | \\tf t8 C4 | C4 | C4 | \\tf no | C4 | C4 '; const score = parseTex(tex); - expect(score.masterBars[0].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[1].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[2].tripletFeel).to.equal(TripletFeel.Triplet16th); - expect(score.masterBars[3].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[4].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[5].tripletFeel).to.equal(TripletFeel.Triplet8th); - expect(score.masterBars[6].tripletFeel).to.equal(TripletFeel.NoTripletFeel); - expect(score.masterBars[7].tripletFeel).to.equal(TripletFeel.NoTripletFeel); - expect(score.masterBars[8].tripletFeel).to.equal(TripletFeel.NoTripletFeel); + expect(score.masterBars[0].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[1].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toBe(TripletFeel.Triplet16th); + expect(score.masterBars[3].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[4].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[5].tripletFeel).toBe(TripletFeel.Triplet8th); + expect(score.masterBars[6].tripletFeel).toBe(TripletFeel.NoTripletFeel); + expect(score.masterBars[7].tripletFeel).toBe(TripletFeel.NoTripletFeel); + expect(score.masterBars[8].tripletFeel).toBe(TripletFeel.NoTripletFeel); testExportRoundtrip(score); }); @@ -996,16 +996,16 @@ describe('AlphaTexImporterTest', () => { let i: number = 0; let b: Beat | null = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; while (b) { - expect(b.duration).to.equal(durations[i], `Duration on beat ${i} was wrong`); + expect(b.duration, `Duration on beat ${i} was wrong`).toBe(durations[i]); if (tuplets[i] === 1) { - expect(b.hasTuplet).to.be.equal(false); + expect(b.hasTuplet).toBe(false); } else { - expect(b.tupletNumerator).to.equal(tuplets[i], `Tuplet on beat ${i} was wrong`); + expect(b.tupletNumerator, `Tuplet on beat ${i} was wrong`).toBe(tuplets[i]); } b = b.nextBeat; i++; } - expect(i).to.equal(durations.length); + expect(i).toBe(durations.length); testExportRoundtrip(score); }); @@ -1018,8 +1018,8 @@ describe('AlphaTexImporterTest', () => { let i: number = 0; let b: Beat | null = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; while (b) { - expect(b.tupletNumerator).to.equal(tupletNumerators[i], `Tuplet on beat ${i} was wrong`); - expect(b.tupletDenominator).to.equal(tupletDenominators[i], `Tuplet on beat ${i} was wrong`); + expect(b.tupletNumerator, `Tuplet on beat ${i} was wrong`).toBe(tupletNumerators[i]); + expect(b.tupletDenominator, `Tuplet on beat ${i} was wrong`).toBe(tupletDenominators[i]); b = b.nextBeat; i++; } @@ -1029,34 +1029,34 @@ describe('AlphaTexImporterTest', () => { it('simple-anacrusis', () => { const tex: string = '\\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; const score = parseTex(tex); - expect(score.masterBars[0].isAnacrusis).to.be.equal(true); - expect(score.masterBars[0].calculateDuration()).to.equal(1920); - expect(score.masterBars[1].calculateDuration()).to.equal(3840); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toBe(1920); + expect(score.masterBars[1].calculateDuration()).toBe(3840); testExportRoundtrip(score); }); it('multi-bar-anacrusis', () => { const tex: string = '\\ac 3.3 3.3 | \\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; const score = parseTex(tex); - expect(score.masterBars[0].isAnacrusis).to.be.equal(true); - expect(score.masterBars[1].isAnacrusis).to.be.equal(true); - expect(score.masterBars[0].calculateDuration()).to.equal(1920); - expect(score.masterBars[1].calculateDuration()).to.equal(1920); - expect(score.masterBars[2].calculateDuration()).to.equal(3840); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[1].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toBe(1920); + expect(score.masterBars[1].calculateDuration()).toBe(1920); + expect(score.masterBars[2].calculateDuration()).toBe(3840); testExportRoundtrip(score); }); it('random-anacrusis', () => { const tex: string = '\\ac 3.3 3.3 | 1.1 2.1 3.1 4.1 | \\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; const score = parseTex(tex); - expect(score.masterBars[0].isAnacrusis).to.be.equal(true); - expect(score.masterBars[1].isAnacrusis).to.be.equal(false); - expect(score.masterBars[2].isAnacrusis).to.be.equal(true); - expect(score.masterBars[3].isAnacrusis).to.be.equal(false); - expect(score.masterBars[0].calculateDuration()).to.equal(1920); - expect(score.masterBars[1].calculateDuration()).to.equal(3840); - expect(score.masterBars[2].calculateDuration()).to.equal(1920); - expect(score.masterBars[3].calculateDuration()).to.equal(3840); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[1].isAnacrusis).toBe(false); + expect(score.masterBars[2].isAnacrusis).toBe(true); + expect(score.masterBars[3].isAnacrusis).toBe(false); + expect(score.masterBars[0].calculateDuration()).toBe(1920); + expect(score.masterBars[1].calculateDuration()).toBe(3840); + expect(score.masterBars[2].calculateDuration()).toBe(1920); + expect(score.masterBars[3].calculateDuration()).toBe(3840); testExportRoundtrip(score); }); @@ -1064,29 +1064,29 @@ describe('AlphaTexImporterTest', () => { const tex: string = '\\ro 1.3 2.3 3.3 4.3 | 5.3 6.3 7.3 8.3 | \\rc 2 1.3 2.3 3.3 4.3 | \\ro \\rc 3 1.3 2.3 3.3 4.3 |'; const score = parseTex(tex); - expect(score.masterBars[0].isRepeatStart).to.be.equal(true); - expect(score.masterBars[1].isRepeatStart).to.be.equal(false); - expect(score.masterBars[2].isRepeatStart).to.be.equal(false); - expect(score.masterBars[3].isRepeatStart).to.be.equal(true); - expect(score.masterBars[0].repeatCount).to.equal(0); - expect(score.masterBars[1].repeatCount).to.equal(0); - expect(score.masterBars[2].repeatCount).to.equal(2); - expect(score.masterBars[3].repeatCount).to.equal(3); + expect(score.masterBars[0].isRepeatStart).toBe(true); + expect(score.masterBars[1].isRepeatStart).toBe(false); + expect(score.masterBars[2].isRepeatStart).toBe(false); + expect(score.masterBars[3].isRepeatStart).toBe(true); + expect(score.masterBars[0].repeatCount).toBe(0); + expect(score.masterBars[1].repeatCount).toBe(0); + expect(score.masterBars[2].repeatCount).toBe(2); + expect(score.masterBars[3].repeatCount).toBe(3); testExportRoundtrip(score); }); it('alternate-endings', () => { const tex: string = '\\ro 4.3*4 | \\ae (1 2 3) 6.3*4 | \\ae 4 \\rc 4 6.3 6.3 6.3 5.3 |'; const score = parseTex(tex); - expect(score.masterBars[0].isRepeatStart).to.be.equal(true); - expect(score.masterBars[1].isRepeatStart).to.be.equal(false); - expect(score.masterBars[2].isRepeatStart).to.be.equal(false); - expect(score.masterBars[0].repeatCount).to.equal(0); - expect(score.masterBars[1].repeatCount).to.equal(0); - expect(score.masterBars[2].repeatCount).to.equal(4); - expect(score.masterBars[0].alternateEndings).to.equal(0b0000); - expect(score.masterBars[1].alternateEndings).to.equal(0b0111); - expect(score.masterBars[2].alternateEndings).to.equal(0b1000); + expect(score.masterBars[0].isRepeatStart).toBe(true); + expect(score.masterBars[1].isRepeatStart).toBe(false); + expect(score.masterBars[2].isRepeatStart).toBe(false); + expect(score.masterBars[0].repeatCount).toBe(0); + expect(score.masterBars[1].repeatCount).toBe(0); + expect(score.masterBars[2].repeatCount).toBe(4); + expect(score.masterBars[0].alternateEndings).toBe(0b0000); + expect(score.masterBars[1].alternateEndings).toBe(0b0111); + expect(score.masterBars[2].alternateEndings).toBe(0b1000); testExportRoundtrip(score); }); @@ -1099,24 +1099,24 @@ describe('AlphaTexImporterTest', () => { \\ae (1 3) 1.1.1 | \\ae 2 \\rc 3 2.1 | `; const score = parseTex(tex); - expect(score.masterBars[0].isRepeatStart).to.be.equal(true); + expect(score.masterBars[0].isRepeatStart).toBe(true); for (let i = 1; i <= 9; i++) { - expect(score.masterBars[i].isRepeatStart).to.be.equal(false); + expect(score.masterBars[i].isRepeatStart).toBe(false); } for (let i = 0; i <= 8; i++) { - expect(score.masterBars[i].repeatCount).to.equal(0); + expect(score.masterBars[i].repeatCount).toBe(0); } - expect(score.masterBars[9].repeatCount).to.equal(3); - expect(score.masterBars[0].alternateEndings).to.equal(0b001); - expect(score.masterBars[1].alternateEndings).to.equal(0b010); - expect(score.masterBars[2].alternateEndings).to.equal(0b100); - expect(score.masterBars[3].alternateEndings).to.equal(0b000); - expect(score.masterBars[4].alternateEndings).to.equal(0b001); - expect(score.masterBars[5].alternateEndings).to.equal(0b010); - expect(score.masterBars[6].alternateEndings).to.equal(0b100); - expect(score.masterBars[7].alternateEndings).to.equal(0b000); - expect(score.masterBars[8].alternateEndings).to.equal(0b101); - expect(score.masterBars[9].alternateEndings).to.equal(0b010); + expect(score.masterBars[9].repeatCount).toBe(3); + expect(score.masterBars[0].alternateEndings).toBe(0b001); + expect(score.masterBars[1].alternateEndings).toBe(0b010); + expect(score.masterBars[2].alternateEndings).toBe(0b100); + expect(score.masterBars[3].alternateEndings).toBe(0b000); + expect(score.masterBars[4].alternateEndings).toBe(0b001); + expect(score.masterBars[5].alternateEndings).toBe(0b010); + expect(score.masterBars[6].alternateEndings).toBe(0b100); + expect(score.masterBars[7].alternateEndings).toBe(0b000); + expect(score.masterBars[8].alternateEndings).toBe(0b101); + expect(score.masterBars[9].alternateEndings).toBe(0b010); testExportRoundtrip(score); }); @@ -1133,101 +1133,101 @@ describe('AlphaTexImporterTest', () => { `; const score = parseTex(tex); - expect(score.tracks[0].staves[0].transpositionPitch).to.equal(0); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); - expect(score.tracks[0].staves[1].transpositionPitch).to.equal(0); - expect(score.tracks[0].staves[1].displayTranspositionPitch).to.equal(0); - expect(score.tracks[1].staves[0].transpositionPitch).to.equal(0); - expect(score.tracks[1].staves[0].displayTranspositionPitch).to.equal(-12); + expect(score.tracks[0].staves[0].transpositionPitch).toBe(0); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); + expect(score.tracks[0].staves[1].transpositionPitch).toBe(0); + expect(score.tracks[0].staves[1].displayTranspositionPitch).toBe(0); + expect(score.tracks[1].staves[0].transpositionPitch).toBe(0); + expect(score.tracks[1].staves[0].displayTranspositionPitch).toBe(-12); testExportRoundtrip(score); }); it('dynamics', () => { const tex: string = '1.1.8{dy ppp} 1.1{dy pp} 1.1{dy p} 1.1{dy mp} 1.1{dy mf} 1.1{dy f} 1.1{dy ff} 1.1{dy fff}'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).to.equal(DynamicValue.PP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).to.equal(DynamicValue.P); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).to.equal(DynamicValue.MP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].dynamics).to.equal(DynamicValue.MF); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].dynamics).to.equal(DynamicValue.F); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].dynamics).to.equal(DynamicValue.FF); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].dynamics).to.equal(DynamicValue.FFF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toBe(DynamicValue.PP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).toBe(DynamicValue.P); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).toBe(DynamicValue.MP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].dynamics).toBe(DynamicValue.MF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].dynamics).toBe(DynamicValue.F); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].dynamics).toBe(DynamicValue.FF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].dynamics).toBe(DynamicValue.FFF); testExportRoundtrip(score); }); it('dynamics-auto', () => { const tex: string = '1.1.4{dy ppp} 1.1 1.1{dy mp} 1.1'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).to.equal(DynamicValue.MP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).to.equal(DynamicValue.MP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).toBe(DynamicValue.MP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).toBe(DynamicValue.MP); testExportRoundtrip(score); }); it('dynamics-auto-reset-on-track', () => { const tex: string = '1.1.4{dy ppp} 1.1 \\track "Second" 1.1.4'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[1].staves[0].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.F); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[1].staves[0].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.F); testExportRoundtrip(score); }); it('dynamics-auto-reset-on-staff', () => { const tex: string = '1.1.4{dy ppp} 1.1 \\staff 1.1.4'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).to.equal(DynamicValue.PPP); - expect(score.tracks[0].staves[1].bars[0].voices[0].beats[0].dynamics).to.equal(DynamicValue.F); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toBe(DynamicValue.PPP); + expect(score.tracks[0].staves[1].bars[0].voices[0].beats[0].dynamics).toBe(DynamicValue.F); testExportRoundtrip(score); }); it('crescendo', () => { const tex: string = '1.1.4{dec} 1.1{dec} 1.1{cre} 1.1{cre}'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].crescendo).to.equal(CrescendoType.Decrescendo); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].crescendo).to.equal(CrescendoType.Decrescendo); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].crescendo).to.equal(CrescendoType.Crescendo); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].crescendo).to.equal(CrescendoType.Crescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].crescendo).toBe(CrescendoType.Decrescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].crescendo).toBe(CrescendoType.Decrescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].crescendo).toBe(CrescendoType.Crescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].crescendo).toBe(CrescendoType.Crescendo); testExportRoundtrip(score); }); it('left-hand-tapping', () => { const tex: string = ':4 1.1{lht} 1.1 1.1{lht} 1.1'; const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isLeftHandTapped).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).to.equal(false); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isLeftHandTapped).to.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLeftHandTapped).to.equal(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLeftHandTapped).toBe(false); testExportRoundtrip(score); }); it('expect-invalid-format-xml', () => { - expect(() => parseTex('')).to.throw(UnsupportedFormatError); + expect(() => parseTex('')).toThrow(UnsupportedFormatError); }); it('expect-invalid-format-other-text', () => { - expect(() => parseTex('This is not an alphaTex file')).to.throw(UnsupportedFormatError); + expect(() => parseTex('This is not an alphaTex file')).toThrow(UnsupportedFormatError); }); it('auto-detect-tuning-from-instrument', () => { let score = parseTex('\\instrument acousticguitarsteel . 3.3'); - expect(score.tracks[0].staves[0].tuning.length).to.equal(6); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(-12); + expect(score.tracks[0].staves[0].tuning.length).toBe(6); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(-12); score = parseTex('\\instrument acousticbass . 3.3'); - expect(score.tracks[0].staves[0].tuning.length).to.equal(4); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(-12); + expect(score.tracks[0].staves[0].tuning.length).toBe(4); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(-12); score = parseTex('\\instrument violin . 3.3'); - expect(score.tracks[0].staves[0].tuning.length).to.equal(4); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); + expect(score.tracks[0].staves[0].tuning.length).toBe(4); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); score = parseTex('\\instrument acousticpiano . C4'); - expect(score.tracks[0].staves[0].tuning.length).to.equal(0); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); + expect(score.tracks[0].staves[0].tuning.length).toBe(0); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); }); it('multibyte-encoding', () => { @@ -1238,20 +1238,20 @@ describe('AlphaTexImporterTest', () => { \\lyrics "Test Lyrics 🤘" (1.2 1.1).4 x.2.8 0.1 1.1 | 1.2 3.2 0.1 1.1`); - expect(score.title).to.equal(multiByteChars); - expect(score.tracks[0].name).to.equal('🎸'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).to.equal('🤘'); + expect(score.title).toBe(multiByteChars); + expect(score.tracks[0].name).toBe('🎸'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).toBe('🤘'); testExportRoundtrip(score); }); function runSectionNoteSymbolTest(noteSymbol: string) { const score = parseTex(`1.3.4 * 4 | \\section "Verse" ${noteSymbol}.1 | 2.3.4*4`); - expect(score.masterBars.length).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(4); - expect(score.masterBars[1].section!.text).to.equal('Verse'); - expect(score.masterBars[1].section!.marker).to.equal(''); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).to.equal(1); + expect(score.masterBars.length).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(4); + expect(score.masterBars[1].section!.text).toBe('Verse'); + expect(score.masterBars[1].section!.marker).toBe(''); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toBe(1); } it('does-not-interpret-note-symbols-on-section', () => { @@ -1275,66 +1275,66 @@ describe('AlphaTexImporterTest', () => { for (const _i of [1, 2]) { importer.initFromString(tex, new Settings()); const score = importer.readScore(); - expect(score.title).to.equal('Test'); - expect(score.words).to.equal('test'); - expect(score.music).to.equal('alphaTab'); - expect(score.copyright).to.equal('test'); - expect(score.tempo).to.equal(200); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].playbackInfo.program).to.equal(30); - expect(score.tracks[0].staves[0].capo).to.equal(2); - expect(score.tracks[0].staves[0].tuning.join(',')).to.equal('55,38,43,47,50,69'); - expect(score.masterBars.length).to.equal(2); + expect(score.title).toBe('Test'); + expect(score.words).toBe('test'); + expect(score.music).toBe('alphaTab'); + expect(score.copyright).toBe('test'); + expect(score.tempo).toBe(200); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].playbackInfo.program).toBe(30); + expect(score.tracks[0].staves[0].capo).toBe(2); + expect(score.tracks[0].staves[0].tuning.join(',')).toBe('55,38,43,47,50,69'); + expect(score.masterBars.length).toBe(2); // bars[0] - expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).to.equal(Duration.Half); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).to.equal(Duration.Quarter); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).to.equal(Duration.Quarter); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).toBe(Duration.Half); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).toBe(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).toBe(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).toBe(3); // bars[1] - expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].string).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].notes.length).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].duration).to.equal(Duration.Half); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).to.equal(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes.length).toBe(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].string).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].notes.length).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].duration).toBe(Duration.Half); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).toBe(true); } }); it('tempo-as-float', () => { const score = parseTex('\\tempo 112.5 .'); - expect(score.tempo).to.equal(112.5); + expect(score.tempo).toBe(112.5); testExportRoundtrip(score); }); it('tempo-as-float-in-bar', () => { const score = parseTex('\\tempo 112 . 3.3.1 | \\tempo 333.3 3.3'); - expect(score.tempo).to.equal(112); - expect(score.tracks[0].staves[0].bars[1].masterBar.tempoAutomations[0]?.value).to.equal(333.3); + expect(score.tempo).toBe(112); + expect(score.tracks[0].staves[0].bars[1].masterBar.tempoAutomations[0]?.value).toBe(333.3); testExportRoundtrip(score); }); @@ -1344,16 +1344,16 @@ describe('AlphaTexImporterTest', () => { . 30 31 33 34 `); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(9); - expect(score.tracks[0].staves[0].isPercussion).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); - expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).to.equal(49); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); - expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).to.equal(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(2); - expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).to.equal(37); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).to.equal(3); - expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).to.equal(38); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(9); + expect(score.tracks[0].staves[0].isPercussion).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).toBe(0); + expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).toBe(49); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).toBe(1); + expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).toBe(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).toBe(2); + expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).toBe(37); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).toBe(3); + expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).toBe(38); testExportRoundtrip(score); }); @@ -1367,16 +1367,16 @@ describe('AlphaTexImporterTest', () => { . A B C D `); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(9); - expect(score.tracks[0].staves[0].isPercussion).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); - expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).to.equal(49); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); - expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).to.equal(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(2); - expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).to.equal(37); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).to.equal(3); - expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).to.equal(38); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(9); + expect(score.tracks[0].staves[0].isPercussion).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).toBe(0); + expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).toBe(49); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).toBe(1); + expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).toBe(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).toBe(2); + expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).toBe(37); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).toBe(3); + expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).toBe(38); testExportRoundtrip(score); }); @@ -1387,16 +1387,16 @@ describe('AlphaTexImporterTest', () => { . "Cymbal (hit)" "Snare (side stick)" "Snare (side stick) 2" "Snare (hit)" `); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(9); - expect(score.tracks[0].staves[0].isPercussion).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); - expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).to.equal(49); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); - expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).to.equal(37); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(2); - expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).to.equal(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).to.equal(3); - expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).to.equal(38); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(9); + expect(score.tracks[0].staves[0].isPercussion).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).toBe(0); + expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).toBe(49); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).toBe(1); + expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).toBe(37); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).toBe(2); + expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).toBe(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).toBe(3); + expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).toBe(38); testExportRoundtrip(score); }); @@ -1407,16 +1407,16 @@ describe('AlphaTexImporterTest', () => { . CymbalHit SnareSideStick SnareSideStick2 SnareHit `); - expect(score.tracks[0].playbackInfo.primaryChannel).to.equal(9); - expect(score.tracks[0].staves[0].isPercussion).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); - expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).to.equal(49); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); - expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).to.equal(37); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(2); - expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).to.equal(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).to.equal(3); - expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).to.equal(38); + expect(score.tracks[0].playbackInfo.primaryChannel).toBe(9); + expect(score.tracks[0].staves[0].isPercussion).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).toBe(0); + expect(score.tracks[0].percussionArticulations[0].outputMidiNumber).toBe(49); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).toBe(1); + expect(score.tracks[0].percussionArticulations[1].outputMidiNumber).toBe(37); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).toBe(2); + expect(score.tracks[0].percussionArticulations[2].outputMidiNumber).toBe(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].percussionArticulation).toBe(3); + expect(score.tracks[0].percussionArticulations[3].outputMidiNumber).toBe(38); testExportRoundtrip(score); }); @@ -1424,11 +1424,11 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` . \\tempo 120 1.1.4 1.1 1.1{tempo 60} 1.1 | 1.1.4{tempo 100} 1.1 1.1{tempo 120} 1.1 `); - expect(score.masterBars[0].tempoAutomations.length).to.equal(2); - expect(score.masterBars[0].tempoAutomations[0].value).to.equal(120); - expect(score.masterBars[0].tempoAutomations[0].ratioPosition).to.equal(0); - expect(score.masterBars[0].tempoAutomations[1].value).to.equal(60); - expect(score.masterBars[0].tempoAutomations[1].ratioPosition).to.equal(0.5); + expect(score.masterBars[0].tempoAutomations.length).toBe(2); + expect(score.masterBars[0].tempoAutomations[0].value).toBe(120); + expect(score.masterBars[0].tempoAutomations[0].ratioPosition).toBe(0); + expect(score.masterBars[0].tempoAutomations[1].value).toBe(60); + expect(score.masterBars[0].tempoAutomations[1].ratioPosition).toBe(0.5); testExportRoundtrip(score); }); @@ -1452,41 +1452,41 @@ describe('AlphaTexImporterTest', () => { b = b.nextBeat; } - expect(actualAccidentalModes.join(',')).to.equal(expectedAccidentalModes.join(',')); + expect(actualAccidentalModes.join(',')).toBe(expectedAccidentalModes.join(',')); testExportRoundtrip(score); }); it('accidental-mode', () => { // song level let score = parseTex('\\accidentals auto . F##4'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.Default ); // track level score = parseTex('\\track "T1" F##4 | \\track "T2" \\accidentals auto F##4'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.ForceDoubleSharp ); - expect(score.tracks[1].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[1].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.Default ); // staff level score = parseTex('\\track "T1" \\staff F##4 \\staff \\accidentals auto F##4'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.ForceDoubleSharp ); - expect(score.tracks[0].staves[1].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[1].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.Default ); // bar level score = parseTex('F##4 | \\accidentals auto F##4'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.ForceDoubleSharp ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].accidentalMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].accidentalMode).toBe( NoteAccidentalMode.Default ); testExportRoundtrip(score); @@ -1494,47 +1494,47 @@ describe('AlphaTexImporterTest', () => { it('dead-slap', () => { const score = parseTex('r { ds }'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isRest).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isRest).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).toBe(true); testExportRoundtrip(score); }); it('golpe', () => { const score = parseTex('3.3 { glpf } 3.3 { glpt }'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].golpe).to.equal(GolpeType.Finger); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].golpe).to.equal(GolpeType.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].golpe).toBe(GolpeType.Finger); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].golpe).toBe(GolpeType.Thumb); testExportRoundtrip(score); }); it('fade', () => { const score = parseTex('3.3 { f } 3.3 { fo } 3.3 { vs } '); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].fade).to.equal(FadeType.FadeIn); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].fade).to.equal(FadeType.FadeOut); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fade).to.equal(FadeType.VolumeSwell); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].fade).toBe(FadeType.FadeIn); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].fade).toBe(FadeType.FadeOut); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fade).toBe(FadeType.VolumeSwell); testExportRoundtrip(score); }); it('barre', () => { const score = parseTex('3.3 { barre 5 } 3.3 { barre 14 half }'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreFret).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreShape).to.equal(BarreShape.Full); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreFret).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreShape).toBe(BarreShape.Full); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].barreFret).to.equal(14); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].barreShape).to.equal(BarreShape.Half); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].barreFret).toBe(14); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].barreShape).toBe(BarreShape.Half); testExportRoundtrip(score); testExportRoundtrip(score); }); it('ornaments', () => { const score = parseTex('3.3 { turn } 3.3 { iturn } 3.3 { umordent } 3.3 { lmordent }'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].ornament).to.equal(NoteOrnament.Turn); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].ornament).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].ornament).toBe(NoteOrnament.Turn); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].ornament).toBe( NoteOrnament.InvertedTurn ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].ornament).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].ornament).toBe( NoteOrnament.UpperMordent ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].ornament).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].ornament).toBe( NoteOrnament.LowerMordent ); testExportRoundtrip(score); @@ -1542,26 +1542,26 @@ describe('AlphaTexImporterTest', () => { it('rasgueado', () => { const score = parseTex('3.3 { rasg mi } 3.3 { rasg pmptriplet } 3.3 { rasg amianapaest }'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].rasgueado).to.equal(Rasgueado.Mi); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].hasRasgueado).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].rasgueado).to.equal(Rasgueado.PmpTriplet); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].hasRasgueado).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].rasgueado).to.equal(Rasgueado.AmiAnapaest); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].hasRasgueado).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].rasgueado).toBe(Rasgueado.Mi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].hasRasgueado).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].rasgueado).toBe(Rasgueado.PmpTriplet); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].hasRasgueado).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].rasgueado).toBe(Rasgueado.AmiAnapaest); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].hasRasgueado).toBe(true); testExportRoundtrip(score); }); it('directions', () => { const score = parseTex('. \\jump Segno | | \\jump DaCapoAlCoda \\jump Coda \\jump SegnoSegno '); - expect(score.masterBars[0].directions).to.be.ok; - expect(score.masterBars[0].directions).to.contain(Direction.TargetSegno); + expect(score.masterBars[0].directions).toBeTruthy(); + expect(score.masterBars[0].directions).toContain(Direction.TargetSegno); - expect(score.masterBars[1].directions).to.not.be.ok; + expect(score.masterBars[1].directions).not.toBeTruthy(); - expect(score.masterBars[2].directions).to.be.ok; - expect(score.masterBars[2].directions).to.contain(Direction.JumpDaCapoAlCoda); - expect(score.masterBars[2].directions).to.contain(Direction.TargetCoda); - expect(score.masterBars[2].directions).to.contain(Direction.TargetSegnoSegno); + expect(score.masterBars[2].directions).toBeTruthy(); + expect(score.masterBars[2].directions).toContain(Direction.JumpDaCapoAlCoda); + expect(score.masterBars[2].directions).toContain(Direction.TargetCoda); + expect(score.masterBars[2].directions).toContain(Direction.TargetSegnoSegno); testExportRoundtrip(score); }); @@ -1575,11 +1575,11 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars.length).to.equal(2); + expect(score.masterBars.length).toBe(2); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).toBe(2); testExportRoundtrip(score); }); @@ -1591,11 +1591,11 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars.length).to.equal(2); + expect(score.masterBars.length).toBe(2); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).toBe(2); testExportRoundtrip(score); }); @@ -1606,11 +1606,11 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars.length).to.equal(2); + expect(score.masterBars.length).toBe(2); - expect(score.tracks[0].staves[0].bars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).toBe(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).toBe(2); testExportRoundtrip(score); }); @@ -1618,7 +1618,7 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` \\staff { score 3 } `); - expect(score.tracks[0].staves[0].standardNotationLineCount).to.equal(3); + expect(score.tracks[0].staves[0].standardNotationLineCount).toBe(3); testExportRoundtrip(score); }); @@ -1628,8 +1628,8 @@ describe('AlphaTexImporterTest', () => { \\instructions "Line1\nLine2" . `); - expect(score.title).to.equal('Title\tTitle'); - expect(score.instructions).to.equal('Line1\nLine2'); + expect(score.title).toBe('Title\tTitle'); + expect(score.instructions).toBe('Line1\nLine2'); testExportRoundtrip(score); }); @@ -1638,8 +1638,8 @@ describe('AlphaTexImporterTest', () => { \\tempo (80 "Label") . `); - expect(score.tempo).to.equal(80); - expect(score.tempoLabel).to.equal('Label'); + expect(score.tempo).toBe(80); + expect(score.tempoLabel).toBe('Label'); testExportRoundtrip(score); }); @@ -1650,8 +1650,8 @@ describe('AlphaTexImporterTest', () => { \\transpose 6 . `); - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(-12); - expect(score.tracks[0].staves[0].transpositionPitch).to.equal(-6); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(-12); + expect(score.tracks[0].staves[0].transpositionPitch).toBe(-6); testExportRoundtrip(score); }); @@ -1659,8 +1659,8 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3.4{v} 3.3.4{vw} `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].vibrato).to.equal(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].vibrato).toBe(VibratoType.Wide); testExportRoundtrip(score); }); @@ -1668,8 +1668,8 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3{v}.4 3.3{vw}.4 `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato).to.equal(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato).toBe(VibratoType.Wide); testExportRoundtrip(score); }); @@ -1678,16 +1678,16 @@ describe('AlphaTexImporterTest', () => { 3.3.4{ tb dive (0 -12.5) } | 3.3.4{ tb dive gradual (0 -12.5) } | `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarType).toBe(WhammyType.Dive); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toBe(-12.5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarType).toBe(WhammyType.Dive); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyStyle).toBe(BendStyle.Gradual); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toBe(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toBe(-12.5); testExportRoundtrip(score); }); @@ -1695,8 +1695,8 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3.4{ ot 15ma } 3.3.4{ ot 8vb } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].ottava).to.equal(Ottavia._15ma); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].ottava).to.equal(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].ottava).toBe(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].ottava).toBe(Ottavia._8vb); testExportRoundtrip(score); }); @@ -1704,8 +1704,8 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3.4{ txt "Hello World" } 3.3.4{ txt Hello } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].text).to.equal('Hello World'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].text).to.equal('Hello'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].text).toBe('Hello World'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].text).toBe('Hello'); testExportRoundtrip(score); }); @@ -1713,8 +1713,8 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3.4{ legatoOrigin } 4.3.4 `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isLegatoOrigin).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].isLegatoDestination).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isLegatoOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].isLegatoDestination).toBe(true); testExportRoundtrip(score); }); @@ -1723,12 +1723,12 @@ describe('AlphaTexImporterTest', () => { \\instrument acousticgrandpiano G4 G4 G4 { instrument brightacousticpiano } `); - expect(score.tracks[0].playbackInfo.program).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( + expect(score.tracks[0].playbackInfo.program).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).toBe( AutomationType.Instrument ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).toBe(1); testExportRoundtrip(score); }); @@ -1736,9 +1736,9 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` G4 G4 G4 { fermata medium 4 } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata!.type).to.equal(FermataType.Medium); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata!.length).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata!.type).toBe(FermataType.Medium); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].fermata!.length).toBe(4); testExportRoundtrip(score); }); @@ -1746,9 +1746,9 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 3.3{ b bend gradual (0 4)} `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).to.equal(BendType.Bend); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).toBe(BendType.Bend); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendStyle).toBe(BendStyle.Gradual); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toBe(2); testExportRoundtrip(score); }); @@ -1756,48 +1756,48 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` 2.3{nh} 2.3{ah} 2.3{ah 7} 2.3{th} 2.3{th 7} 2.3{ph} 2.3{ph 7} 2.3{sh} 2.3{sh 7} 2.3{fh} 2.3{fh 7} `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toBe( HarmonicType.Natural ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicValue).to.equal(2.4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicValue).toBe(2.4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicValue).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicValue).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicValue).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicValue).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).to.equal(HarmonicType.Tap); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicValue).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicValue).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).to.equal(HarmonicType.Tap); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicValue).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicValue).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].harmonicType).to.equal(HarmonicType.Pinch); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].harmonicValue).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].harmonicType).toBe(HarmonicType.Pinch); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].harmonicValue).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].harmonicType).to.equal(HarmonicType.Pinch); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].harmonicValue).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].harmonicType).toBe(HarmonicType.Pinch); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].harmonicValue).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].harmonicType).to.equal(HarmonicType.Semi); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].harmonicValue).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].harmonicType).toBe(HarmonicType.Semi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].harmonicValue).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].harmonicType).to.equal(HarmonicType.Semi); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].harmonicValue).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].harmonicType).toBe(HarmonicType.Semi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].harmonicValue).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].harmonicType).toBe( HarmonicType.Feedback ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].harmonicValue).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].harmonicValue).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[10].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[10].notes[0].harmonicType).toBe( HarmonicType.Feedback ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[10].notes[0].harmonicValue).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[10].notes[0].harmonicValue).toBe(7); testExportRoundtrip(score); }); @@ -1805,9 +1805,9 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` \\ts common `); - expect(score.masterBars[0].timeSignatureNumerator).to.equal(4); - expect(score.masterBars[0].timeSignatureDenominator).to.equal(4); - expect(score.masterBars[0].timeSignatureCommon).to.be.true; + expect(score.masterBars[0].timeSignatureNumerator).toBe(4); + expect(score.masterBars[0].timeSignatureDenominator).toBe(4); + expect(score.masterBars[0].timeSignatureCommon).toBe(true); testExportRoundtrip(score); }); @@ -1815,7 +1815,7 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` \\ottava 15ma `); - expect(score.tracks[0].staves[0].bars[0].clefOttava).to.equal(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[0].clefOttava).toBe(Ottavia._15ma); testExportRoundtrip(score); }); @@ -1823,7 +1823,7 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` \\simile simple `); - expect(score.tracks[0].staves[0].bars[0].simileMark).to.equal(SimileMark.Simple); + expect(score.tracks[0].staves[0].bars[0].simileMark).toBe(SimileMark.Simple); testExportRoundtrip(score); }); @@ -1833,19 +1833,19 @@ describe('AlphaTexImporterTest', () => { . 3.3.4 * 4 | \\tempo (80 "T2") 4.3.4*4 `); - expect(score.tempo).to.equal(100); - expect(score.tempoLabel).to.equal('T1'); + expect(score.tempo).toBe(100); + expect(score.tempoLabel).toBe('T1'); - expect(score.masterBars[1].tempoAutomations.length).to.equal(1); - expect(score.masterBars[1].tempoAutomations[0].value).to.equal(80); - expect(score.masterBars[1].tempoAutomations[0].text).to.equal('T2'); + expect(score.masterBars[1].tempoAutomations.length).toBe(1); + expect(score.masterBars[1].tempoAutomations[0].value).toBe(80); + expect(score.masterBars[1].tempoAutomations[0].text).toBe('T2'); testExportRoundtrip(score); }); it('double-bar', () => { const tex: string = '3.3 3.3 3.3 3.3 | \\db 1.1 2.1 3.1 4.1'; const score = parseTex(tex); - expect(score.masterBars[1].isDoubleBar).to.be.equal(true); + expect(score.masterBars[1].isDoubleBar).toBe(true); testExportRoundtrip(score); }); @@ -1865,20 +1865,20 @@ describe('AlphaTexImporterTest', () => { . `); - expect(score.defaultSystemsLayout).to.equal(5); - expect(score.systemsLayout.length).to.equal(3); - expect(score.systemsLayout[0]).to.equal(3); - expect(score.systemsLayout[1]).to.equal(2); - expect(score.systemsLayout[2]).to.equal(3); - expect(score.stylesheet.hideDynamics).to.be.true; - expect(score.stylesheet.bracketExtendMode).to.equal(BracketExtendMode.NoBrackets); - expect(score.stylesheet.useSystemSignSeparator).to.be.true; - expect(score.stylesheet.singleTrackTrackNamePolicy).to.equal(TrackNamePolicy.AllSystems); - expect(score.stylesheet.multiTrackTrackNamePolicy).to.equal(TrackNamePolicy.Hidden); - expect(score.stylesheet.firstSystemTrackNameMode).to.equal(TrackNameMode.FullName); - expect(score.stylesheet.otherSystemsTrackNameMode).to.equal(TrackNameMode.FullName); - expect(score.stylesheet.firstSystemTrackNameOrientation).to.equal(TrackNameOrientation.Horizontal); - expect(score.stylesheet.otherSystemsTrackNameOrientation).to.equal(TrackNameOrientation.Horizontal); + expect(score.defaultSystemsLayout).toBe(5); + expect(score.systemsLayout.length).toBe(3); + expect(score.systemsLayout[0]).toBe(3); + expect(score.systemsLayout[1]).toBe(2); + expect(score.systemsLayout[2]).toBe(3); + expect(score.stylesheet.hideDynamics).toBe(true); + expect(score.stylesheet.bracketExtendMode).toBe(BracketExtendMode.NoBrackets); + expect(score.stylesheet.useSystemSignSeparator).toBe(true); + expect(score.stylesheet.singleTrackTrackNamePolicy).toBe(TrackNamePolicy.AllSystems); + expect(score.stylesheet.multiTrackTrackNamePolicy).toBe(TrackNamePolicy.Hidden); + expect(score.stylesheet.firstSystemTrackNameMode).toBe(TrackNameMode.FullName); + expect(score.stylesheet.otherSystemsTrackNameMode).toBe(TrackNameMode.FullName); + expect(score.stylesheet.firstSystemTrackNameOrientation).toBe(TrackNameOrientation.Horizontal); + expect(score.stylesheet.otherSystemsTrackNameOrientation).toBe(TrackNameOrientation.Horizontal); testExportRoundtrip(score); }); @@ -1887,8 +1887,8 @@ describe('AlphaTexImporterTest', () => { 3.3.4 | \\scale 0.5 3.3.4 | \\width 300 3.3.4 `); - expect(score.masterBars[1].displayScale).to.equal(0.5); - expect(score.masterBars[2].displayWidth).to.equal(300); + expect(score.masterBars[1].displayScale).toBe(0.5); + expect(score.masterBars[2].displayWidth).toBe(300); testExportRoundtrip(score); }); @@ -1905,16 +1905,16 @@ describe('AlphaTexImporterTest', () => { } `); - expect(score.tracks[0].color.rgba).to.equal('#FF0000'); - expect(score.tracks[0].defaultSystemsLayout).to.equal(6); - expect(score.tracks[0].systemsLayout.length).to.equal(3); - expect(score.tracks[0].systemsLayout[0]).to.equal(3); - expect(score.tracks[0].systemsLayout[1]).to.equal(2); - expect(score.tracks[0].systemsLayout[0]).to.equal(3); - expect(score.tracks[0].playbackInfo.volume).to.equal(7); - expect(score.tracks[0].playbackInfo.balance).to.equal(3); - expect(score.tracks[0].playbackInfo.isMute).to.be.true; - expect(score.tracks[0].playbackInfo.isSolo).to.be.true; + expect(score.tracks[0].color.rgba).toBe('#FF0000'); + expect(score.tracks[0].defaultSystemsLayout).toBe(6); + expect(score.tracks[0].systemsLayout.length).toBe(3); + expect(score.tracks[0].systemsLayout[0]).toBe(3); + expect(score.tracks[0].systemsLayout[1]).toBe(2); + expect(score.tracks[0].systemsLayout[0]).toBe(3); + expect(score.tracks[0].playbackInfo.volume).toBe(7); + expect(score.tracks[0].playbackInfo.balance).toBe(3); + expect(score.tracks[0].playbackInfo.isMute).toBe(true); + expect(score.tracks[0].playbackInfo.isSolo).toBe(true); testExportRoundtrip(score); }); @@ -1928,16 +1928,16 @@ describe('AlphaTexImporterTest', () => { 3.3{ beam merge } 3.3 | `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).to.be.true; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).toBe( BeamDirection.Down ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); testExportRoundtrip(score); @@ -1948,7 +1948,7 @@ describe('AlphaTexImporterTest', () => { :8 3.3{ string } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].showStringNumber).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].showStringNumber).toBe(true); testExportRoundtrip(score); }); @@ -1957,7 +1957,7 @@ describe('AlphaTexImporterTest', () => { :8 3.3{ hide } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isVisible).to.be.false; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isVisible).toBe(false); }); it('note-slur', () => { @@ -1965,8 +1965,8 @@ describe('AlphaTexImporterTest', () => { :8 (3.3{ slur s1 } 3.4 3.5) (10.3 {slur s1} 17.4 15.5) `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isSlurOrigin).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isSlurDestination).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isSlurOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isSlurDestination).toBe(true); testExportRoundtrip(score); }); @@ -1978,10 +1978,10 @@ describe('AlphaTexImporterTest', () => { \\tuning A1 D2 A2 D3 G3 B3 E4 hide 4.1 3.1 2.1 1.1`); - expect(score.tracks[1].staves[0].stringTuning.tunings[0]).to.equal(33); - expect(score.stylesheet.perTrackDisplayTuning).to.be.ok; - expect(score.stylesheet.perTrackDisplayTuning!.has(1)).to.be.true; - expect(score.stylesheet.perTrackDisplayTuning!.get(1)).to.be.false; + expect(score.tracks[1].staves[0].stringTuning.tunings[0]).toBe(33); + expect(score.stylesheet.perTrackDisplayTuning).toBeTruthy(); + expect(score.stylesheet.perTrackDisplayTuning!.has(1)).toBe(true); + expect(score.stylesheet.perTrackDisplayTuning!.get(1)).toBe(false); testExportRoundtrip(score); }); @@ -1995,31 +1995,26 @@ describe('AlphaTexImporterTest', () => { \\clef "n" | \\clef "alto" | \\clef "tenor" | \\clef "bass" | \\clef "treble" `); let barIndex = 0; - expect(score.tracks[0].staves[0].bars[barIndex].clef).to.equal(Clef.C4); - expect(score.tracks[0].staves[0].bars[barIndex++].clefOttava).to.equal(Ottavia._15ma); - expect(score.tracks[0].staves[0].bars[barIndex].clef).to.equal(Clef.C4); - expect(score.tracks[0].staves[0].bars[barIndex++].clefOttava).to.equal(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[barIndex].clef).toBe(Clef.C4); + expect(score.tracks[0].staves[0].bars[barIndex++].clefOttava).toBe(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[barIndex].clef).toBe(Clef.C4); + expect(score.tracks[0].staves[0].bars[barIndex++].clefOttava).toBe(Ottavia._15ma); for (let i = 0; i < 5; i++) { - expect(score.tracks[0].staves[0].bars[barIndex++].clef).to.equal( - Clef.Neutral, - `Invalid clef at index ${barIndex - 1}` + expect(score.tracks[0].staves[0].bars[barIndex++].clef, `Invalid clef at index ${barIndex - 1}`).toBe( + Clef.Neutral ); - expect(score.tracks[0].staves[0].bars[barIndex++].clef).to.equal( - Clef.C3, - `Invalid clef at index ${barIndex - 1}` + expect(score.tracks[0].staves[0].bars[barIndex++].clef, `Invalid clef at index ${barIndex - 1}`).toBe( + Clef.C3 ); - expect(score.tracks[0].staves[0].bars[barIndex++].clef).to.equal( - Clef.C4, - `Invalid clef at index ${barIndex - 1}` + expect(score.tracks[0].staves[0].bars[barIndex++].clef, `Invalid clef at index ${barIndex - 1}`).toBe( + Clef.C4 ); - expect(score.tracks[0].staves[0].bars[barIndex++].clef).to.equal( - Clef.F4, - `Invalid clef at index ${barIndex - 1}` + expect(score.tracks[0].staves[0].bars[barIndex++].clef, `Invalid clef at index ${barIndex - 1}`).toBe( + Clef.F4 ); - expect(score.tracks[0].staves[0].bars[barIndex++].clef).to.equal( - Clef.G2, - `Invalid clef at index ${barIndex - 1}` + expect(score.tracks[0].staves[0].bars[barIndex++].clef, `Invalid clef at index ${barIndex - 1}`).toBe( + Clef.G2 ); } @@ -2036,10 +2031,10 @@ describe('AlphaTexImporterTest', () => { 3.3 `); - expect(score.stylesheet.multiTrackMultiBarRest).to.be.true; - expect(score.stylesheet.perTrackMultiBarRest).to.be.ok; - expect(score.stylesheet.perTrackMultiBarRest!.has(0)).to.be.true; - expect(score.stylesheet.perTrackMultiBarRest!.has(1)).to.be.false; + expect(score.stylesheet.multiTrackMultiBarRest).toBe(true); + expect(score.stylesheet.perTrackMultiBarRest).toBeTruthy(); + expect(score.stylesheet.perTrackMultiBarRest!.has(0)).toBe(true); + expect(score.stylesheet.perTrackMultiBarRest!.has(1)).toBe(false); testExportRoundtrip(score); }); @@ -2058,63 +2053,63 @@ describe('AlphaTexImporterTest', () => { . `); - expect(score.style).to.be.ok; + expect(score.style).toBeTruthy(); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).to.equal('Title: %TITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).toBe('Title: %TITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).to.equal('Subtitle: %SUBTITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).toBe('Subtitle: %SUBTITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).to.equal('Artist: %ARTIST%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).toBe('Artist: %ARTIST%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).to.equal('Album: %ALBUM%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).toBe('Album: %ALBUM%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).to.equal('Words: %WORDS%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).toBe('Words: %WORDS%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).to.equal('Music: %MUSIC%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).toBe('Music: %MUSIC%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).toBe( 'Words & Music: %MUSIC%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.template).toBe( 'Transcriber: %TABBER%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).toBe( 'Copyright: %COPYRIGHT%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).to.equal('Copyright2'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).toBe('Copyright2'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).toBe( TextAlign.Right ); testExportRoundtrip(score); @@ -2203,10 +2198,10 @@ describe('AlphaTexImporterTest', () => { \\tuning E4 B3 G3 D3 A2 E2 "Default" `); - expect(score.tracks[0].staves[0].stringTuning.tunings.join(',')).to.equal( + expect(score.tracks[0].staves[0].stringTuning.tunings.join(',')).toBe( Tuning.getDefaultTuningFor(6)!.tunings.join(',') ); - expect(score.tracks[0].staves[0].stringTuning.name).to.equal('Default'); + expect(score.tracks[0].staves[0].stringTuning.name).toBe('Default'); testExportRoundtrip(score); }); @@ -2218,19 +2213,19 @@ describe('AlphaTexImporterTest', () => { G4 G4 { volume 8 } G4 { volume 9 } `); - expect(score.tracks[0].playbackInfo.volume).to.equal(7); + expect(score.tracks[0].playbackInfo.volume).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).toBe( AutomationType.Volume ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).toBe(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).toBe( AutomationType.Volume ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).to.equal(9); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).toBe(9); testExportRoundtrip(score); }); @@ -2242,19 +2237,19 @@ describe('AlphaTexImporterTest', () => { G4 G4 { balance 8 } G4 { balance 9 } `); - expect(score.tracks[0].playbackInfo.balance).to.equal(7); + expect(score.tracks[0].playbackInfo.balance).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).toBe( AutomationType.Balance ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).toBe(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).toBe( AutomationType.Balance ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).to.equal(9); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].value).toBe(9); testExportRoundtrip(score); }); @@ -2263,8 +2258,8 @@ describe('AlphaTexImporterTest', () => { 3.3.4 { barre 5 half } `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreFret).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreShape).to.equal(BarreShape.Half); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreFret).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].barreShape).toBe(BarreShape.Half); testExportRoundtrip(score); }); @@ -2273,8 +2268,8 @@ describe('AlphaTexImporterTest', () => { ().16 {ds} `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(0); testExportRoundtrip(score); }); @@ -2284,13 +2279,13 @@ describe('AlphaTexImporterTest', () => { . `); - expect(score.title).to.equal('😸'); + expect(score.title).toBe('😸'); }); it('utf16', () => { const score = parseTex(`\\title "🤘🏻" .`); - expect(score.title).to.equal('🤘🏻'); + expect(score.title).toBe('🤘🏻'); }); it('beat-lyrics', () => { @@ -2302,21 +2297,21 @@ describe('AlphaTexImporterTest', () => { 3.3.4 {lyrics 0 "E" lyrics 1 "F" lyrics 2 "G"} `); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].lyrics).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].lyrics).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics!.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics![0]).to.equal('A'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics!.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics![0]).toBe('A'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics!.length).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).to.equal('B C D'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics!.length).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).toBe('B C D'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics!.length).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![0]).to.equal('E'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![1]).to.equal('F'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![2]).to.equal('G'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics!.length).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![0]).toBe('E'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![1]).toBe('F'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![2]).toBe('G'); testExportRoundtrip(score); }); @@ -2330,11 +2325,11 @@ describe('AlphaTexImporterTest', () => { c4 d4 e4 f4 `); - expect(score.tracks[0].playbackInfo.program).to.equal(4); - expect(score.tracks[0].playbackInfo.bank).to.equal(0); + expect(score.tracks[0].playbackInfo.program).toBe(4); + expect(score.tracks[0].playbackInfo.bank).toBe(0); - expect(score.tracks[1].playbackInfo.program).to.equal(4); - expect(score.tracks[1].playbackInfo.bank).to.equal(2); + expect(score.tracks[1].playbackInfo.program).toBe(4); + expect(score.tracks[1].playbackInfo.bank).toBe(2); testExportRoundtrip(score); }); @@ -2442,7 +2437,7 @@ describe('AlphaTexImporterTest', () => { const importer = new AlphaTexImporter(); importer.initFromString(tex, new Settings()); const s = importer.readScore(); - expect(importer.getStaffNoteKind(s.tracks[0].staves[0])).to.equal(type); + expect(importer.getStaffNoteKind(s.tracks[0].staves[0])).toBe(type); } // - direct values @@ -2488,7 +2483,7 @@ describe('AlphaTexImporterTest', () => { 0.3.4 2.3.4 5.3.4 7.3.4 `); - expect(score.stylesheet.extendBarLines).to.be.true; + expect(score.stylesheet.extendBarLines).toBe(true); testExportRoundtrip(score); }); @@ -2537,21 +2532,21 @@ describe('AlphaTexImporterTest', () => { \\chord ("E" 0 0 1 2 2 0) (0.1 0.2 1.3 2.4 2.5 0.6){ch "E"} `); - expect(score.stylesheet.globalDisplayChordDiagramsInScore).to.be.true; + expect(score.stylesheet.globalDisplayChordDiagramsInScore).toBe(true); score = parseTex(` \\chordDiagramsInScore true \\chord ("E" 0 0 1 2 2 0) (0.1 0.2 1.3 2.4 2.5 0.6){ch "E"} `); - expect(score.stylesheet.globalDisplayChordDiagramsInScore).to.be.true; + expect(score.stylesheet.globalDisplayChordDiagramsInScore).toBe(true); score = parseTex(` \\chordDiagramsInScore false \\chord ("E" 0 0 1 2 2 0) (0.1 0.2 1.3 2.4 2.5 0.6){ch "E"} `); - expect(score.stylesheet.globalDisplayChordDiagramsInScore).to.be.false; + expect(score.stylesheet.globalDisplayChordDiagramsInScore).toBe(false); }); it('empty-staff-options', () => { @@ -2559,29 +2554,29 @@ describe('AlphaTexImporterTest', () => { \\hideEmptyStaves C4 `); - expect(score.stylesheet.hideEmptyStaves).to.be.true; - expect(score.stylesheet.hideEmptyStavesInFirstSystem).to.be.false; - expect(score.stylesheet.showSingleStaffBrackets).to.be.false; + expect(score.stylesheet.hideEmptyStaves).toBe(true); + expect(score.stylesheet.hideEmptyStavesInFirstSystem).toBe(false); + expect(score.stylesheet.showSingleStaffBrackets).toBe(false); score = parseTex(` \\hideEmptyStaves \\hideEmptyStavesInFirstSystem `); - expect(score.stylesheet.hideEmptyStaves).to.be.true; - expect(score.stylesheet.hideEmptyStavesInFirstSystem).to.be.true; + expect(score.stylesheet.hideEmptyStaves).toBe(true); + expect(score.stylesheet.hideEmptyStavesInFirstSystem).toBe(true); score = parseTex(` \\hideEmptyStavesInFirstSystem C4 `); - expect(score.stylesheet.hideEmptyStaves).to.be.false; - expect(score.stylesheet.hideEmptyStavesInFirstSystem).to.be.true; + expect(score.stylesheet.hideEmptyStaves).toBe(false); + expect(score.stylesheet.hideEmptyStavesInFirstSystem).toBe(true); score = parseTex(` \\showSingleStaffBrackets C4 `); - expect(score.stylesheet.showSingleStaffBrackets).to.be.true; + expect(score.stylesheet.showSingleStaffBrackets).toBe(true); }); describe('tremolos', () => { @@ -2623,7 +2618,7 @@ describe('AlphaTexImporterTest', () => { describe('defaultBarNumberDisplay', () => { function test(tex: string, mode: BarNumberDisplay) { const score = parseTex(tex); - expect(score.stylesheet.barNumberDisplay).to.equal(mode); + expect(score.stylesheet.barNumberDisplay).toBe(mode); testExportRoundtrip(score); } @@ -2636,8 +2631,8 @@ describe('AlphaTexImporterTest', () => { describe('barNumberDisplay', () => { function test(tex: string, mode: BarNumberDisplay | undefined) { const score = parseTex(tex); - expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).to.be.undefined; - expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).to.equal(mode); + expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).toBeUndefined(); + expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).toBe(mode); testExportRoundtrip(score); } diff --git a/packages/alphatab/test/importer/AlphaTexLexer.test.ts b/packages/alphatab/test/importer/AlphaTexLexer.test.ts index bf97f0ddd..ace725e35 100644 --- a/packages/alphatab/test/importer/AlphaTexLexer.test.ts +++ b/packages/alphatab/test/importer/AlphaTexLexer.test.ts @@ -1,6 +1,6 @@ +import { describe, expect, it } from 'vitest'; import { type AlphaTexAstNode, AlphaTexNodeType, type AlphaTexNumberLiteral } from '@coderline/alphatab/importer/alphaTex/AlphaTexAst'; import { AlphaTexLexer } from '@coderline/alphatab/importer/alphaTex/AlphaTexLexer'; -import { expect } from 'chai'; describe('AlphaTexLexerTest', () => { function lexerTest(source: string, diagnostics: boolean = false) { diff --git a/packages/alphatab/test/importer/AlphaTexParameter.test.ts b/packages/alphatab/test/importer/AlphaTexParameter.test.ts index d20fb79a1..4da92df88 100644 --- a/packages/alphatab/test/importer/AlphaTexParameter.test.ts +++ b/packages/alphatab/test/importer/AlphaTexParameter.test.ts @@ -1,7 +1,7 @@ +import { describe, expect, it } from 'vitest'; import { AlphaTexParseMode, AlphaTexParser } from '@coderline/alphatab/importer/alphaTex/AlphaTexParser'; import { AlphaTexImporter } from '@coderline/alphatab/importer/AlphaTexImporter'; import { Settings } from '@coderline/alphatab/Settings'; -import { expect } from 'chai'; describe('AlphaTexParameterTests', () => { describe('parser', () => { @@ -9,7 +9,7 @@ describe('AlphaTexParameterTests', () => { const parser = new AlphaTexParser(tex); parser.mode = AlphaTexParseMode.Full; const node = parser.read(); - expect(node).to.be.ok; + expect(node).toBeTruthy(); expect(node).toMatchSnapshot(); expect(parser.lexerDiagnostics.errors).toMatchSnapshot('lexer-diagnostics'); expect(parser.parserDiagnostics.errors).toMatchSnapshot('parser-diagnostics'); @@ -132,7 +132,7 @@ describe('AlphaTexParameterTests', () => { // ignore } - expect(importer.scoreNode).to.be.ok; + expect(importer.scoreNode).toBeTruthy(); expect(importer.scoreNode).toMatchSnapshot(); expect(importer.lexerDiagnostics.errors).toMatchSnapshot('lexer-diagnostics'); expect(importer.parserDiagnostics.errors).toMatchSnapshot('parser-diagnostics'); diff --git a/packages/alphatab/test/importer/AlphaTexParser.test.ts b/packages/alphatab/test/importer/AlphaTexParser.test.ts index c94d939e1..d7d6bf94a 100644 --- a/packages/alphatab/test/importer/AlphaTexParser.test.ts +++ b/packages/alphatab/test/importer/AlphaTexParser.test.ts @@ -1,12 +1,12 @@ import { AlphaTexParseMode, AlphaTexParser } from '@coderline/alphatab/importer/alphaTex/AlphaTexParser'; -import { expect } from 'chai'; +import { describe, expect, it } from 'vitest'; describe('AlphaTexParserTest', () => { function parserTest(source: string) { const parser = new AlphaTexParser(source); parser.mode = AlphaTexParseMode.Full; const node = parser.read(); - expect(node).to.be.ok; + expect(node).toBeTruthy(); expect(node).toMatchSnapshot(); expect(parser.lexerDiagnostics.errors).toMatchSnapshot('lexer-diagnostics'); expect(parser.parserDiagnostics.errors).toMatchSnapshot('parser-diagnostics'); diff --git a/packages/alphatab/test/importer/BinaryStylesheet.test.ts b/packages/alphatab/test/importer/BinaryStylesheet.test.ts index da809f79f..6931185b1 100644 --- a/packages/alphatab/test/importer/BinaryStylesheet.test.ts +++ b/packages/alphatab/test/importer/BinaryStylesheet.test.ts @@ -1,212 +1,211 @@ +import { describe, expect, it } from 'vitest'; import { BinaryStylesheet } from '@coderline/alphatab/importer/BinaryStylesheet'; import type { Color } from '@coderline/alphatab/model/Color'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - describe('BinaryStylesheetParserTest', () => { it('testRead', async () => { const data = await TestPlatform.loadFile('test-data/guitarpro7/BinaryStylesheet'); - const stylesheet: BinaryStylesheet = new BinaryStylesheet(data); + const stylesheet: BinaryStylesheet = new BinaryStylesheet(data, 1280000); - expect(stylesheet.raw.has('Global/chordNameStyle')).to.be.true; - expect(stylesheet.raw.get('Global/chordNameStyle')).to.equal(2); + expect(stylesheet.raw.has('Global/chordNameStyle')).toBe(true); + expect(stylesheet.raw.get('Global/chordNameStyle')).toBe(2); - expect(stylesheet.raw.has('StandardNotation/deadNoteSymbol')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/deadNoteSymbol')).to.equal(0); + expect(stylesheet.raw.has('StandardNotation/deadNoteSymbol')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/deadNoteSymbol')).toBe(0); - expect(stylesheet.raw.has('Header/WordsAndMusic')).to.be.true; - expect(stylesheet.raw.get('Header/WordsAndMusic')).to.equal('Words & Music by %MUSIC%'); + expect(stylesheet.raw.has('Header/WordsAndMusic')).toBe(true); + expect(stylesheet.raw.get('Header/WordsAndMusic')).toBe('Words & Music by %MUSIC%'); - expect(stylesheet.raw.has('Global/PickStrokePriority')).to.be.true; - expect(stylesheet.raw.get('Global/PickStrokePriority')).to.equal(1100); + expect(stylesheet.raw.has('Global/PickStrokePriority')).toBe(true); + expect(stylesheet.raw.get('Global/PickStrokePriority')).toBe(1100); - expect(stylesheet.raw.has('Odd/drawOddFooter')).to.be.true; - expect(stylesheet.raw.get('Odd/drawOddFooter')).to.equal(true); + expect(stylesheet.raw.has('Odd/drawOddFooter')).toBe(true); + expect(stylesheet.raw.get('Odd/drawOddFooter')).toBe(true); - expect(stylesheet.raw.has('TablatureNotation/tabRhythmPlacementVoice3')).to.be.true; - expect(stylesheet.raw.get('TablatureNotation/tabRhythmPlacementVoice3')).to.equal(2); + expect(stylesheet.raw.has('TablatureNotation/tabRhythmPlacementVoice3')).toBe(true); + expect(stylesheet.raw.get('TablatureNotation/tabRhythmPlacementVoice3')).toBe(2); - expect(stylesheet.raw.has('Global/HideTupletBracket')).to.be.true; - expect(stylesheet.raw.get('Global/HideTupletBracket')).to.equal(true); + expect(stylesheet.raw.has('Global/HideTupletBracket')).toBe(true); + expect(stylesheet.raw.get('Global/HideTupletBracket')).toBe(true); - expect(stylesheet.raw.has('Global/DrawChords')).to.be.true; - expect(stylesheet.raw.get('Global/DrawChords')).to.equal(true); + expect(stylesheet.raw.has('Global/DrawChords')).toBe(true); + expect(stylesheet.raw.get('Global/DrawChords')).toBe(true); - expect(stylesheet.raw.has('System/codaSplitWidth')).to.be.true; - expect(stylesheet.raw.get('System/codaSplitWidth') as number).to.be.closeTo(6.0, 0.0001); + expect(stylesheet.raw.has('System/codaSplitWidth')).toBe(true); + expect(stylesheet.raw.get('System/codaSplitWidth') as number).toBeCloseTo(6.0, 3); - expect(stylesheet.raw.has('Global/HarmonicPriority')).to.be.true; - expect(stylesheet.raw.get('Global/HarmonicPriority')).to.equal(2200); + expect(stylesheet.raw.has('Global/HarmonicPriority')).toBe(true); + expect(stylesheet.raw.get('Global/HarmonicPriority')).toBe(2200); - expect(stylesheet.raw.has('Global/LetRingThroughoutPriority')).to.be.true; - expect(stylesheet.raw.get('Global/LetRingThroughoutPriority')).to.equal(2500); + expect(stylesheet.raw.has('Global/LetRingThroughoutPriority')).toBe(true); + expect(stylesheet.raw.get('Global/LetRingThroughoutPriority')).toBe(2500); - expect(stylesheet.raw.has('Global/stretchFactor')).to.be.true; - expect(stylesheet.raw.get('Global/stretchFactor') as number).to.be.closeTo(1, 0.0001); + expect(stylesheet.raw.has('Global/stretchFactor')).toBe(true); + expect(stylesheet.raw.get('Global/stretchFactor') as number).toBeCloseTo(1, 3); - expect(stylesheet.raw.has('StandardNotation/bendHeight')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/bendHeight') as number).to.be.closeTo(2.0, 0.0001); + expect(stylesheet.raw.has('StandardNotation/bendHeight')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/bendHeight') as number).toBeCloseTo(2.0, 3); - expect(stylesheet.raw.has('Global/ChordDiagramPriority')).to.be.true; - expect(stylesheet.raw.get('Global/ChordDiagramPriority')).to.equal(3000); + expect(stylesheet.raw.has('Global/ChordDiagramPriority')).toBe(true); + expect(stylesheet.raw.get('Global/ChordDiagramPriority')).toBe(3000); - expect(stylesheet.raw.has('Global/AlternateEndingPriority')).to.be.true; - expect(stylesheet.raw.get('Global/AlternateEndingPriority')).to.equal(2800); + expect(stylesheet.raw.has('Global/AlternateEndingPriority')).toBe(true); + expect(stylesheet.raw.get('Global/AlternateEndingPriority')).toBe(2800); - expect(stylesheet.raw.has('StandardNotation/tieOffsetX')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/tieOffsetX') as number).to.be.closeTo(0.07999999, 0.0001); + expect(stylesheet.raw.has('StandardNotation/tieOffsetX')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/tieOffsetX') as number).toBeCloseTo(0.07999999, 3); - expect(stylesheet.raw.has('Global/PalmMutePriority')).to.be.true; - expect(stylesheet.raw.get('Global/PalmMutePriority')).to.equal(1200); + expect(stylesheet.raw.has('Global/PalmMutePriority')).toBe(true); + expect(stylesheet.raw.get('Global/PalmMutePriority')).toBe(1200); - expect(stylesheet.raw.has('System/hideLyrics')).to.be.true; - expect(stylesheet.raw.get('System/hideLyrics')).to.equal(false); + expect(stylesheet.raw.has('System/hideLyrics')).toBe(true); + expect(stylesheet.raw.get('System/hideLyrics')).toBe(false); - expect(stylesheet.raw.has('Global/drawArpeggioArrow')).to.be.true; - expect(stylesheet.raw.get('Global/drawArpeggioArrow')).to.equal(true); + expect(stylesheet.raw.has('Global/drawArpeggioArrow')).toBe(true); + expect(stylesheet.raw.get('Global/drawArpeggioArrow')).toBe(true); - expect(stylesheet.raw.has('Global/HoPoPriority')).to.be.true; - expect(stylesheet.raw.get('Global/HoPoPriority')).to.equal(800); + expect(stylesheet.raw.has('Global/HoPoPriority')).toBe(true); + expect(stylesheet.raw.get('Global/HoPoPriority')).toBe(800); - expect(stylesheet.raw.has('Staff/repeatWidth')).to.be.true; - expect(stylesheet.raw.get('Staff/repeatWidth') as number).to.be.closeTo(0.5, 0.0001); + expect(stylesheet.raw.has('Staff/repeatWidth')).toBe(true); + expect(stylesheet.raw.get('Staff/repeatWidth') as number).toBeCloseTo(0.5, 3); - expect(stylesheet.raw.has('System/bracketWidth')).to.be.true; - expect(stylesheet.raw.get('System/bracketWidth') as number).to.be.closeTo(0.5, 0.0001); + expect(stylesheet.raw.has('System/bracketWidth')).toBe(true); + expect(stylesheet.raw.get('System/bracketWidth') as number).toBeCloseTo(0.5, 3); - expect(stylesheet.raw.has('Global/TuningSpaceInFrontOfStaff')).to.be.true; - expect(stylesheet.raw.get('Global/TuningSpaceInFrontOfStaff') as number).to.be.closeTo(2.0, 0.0001); + expect(stylesheet.raw.has('Global/TuningSpaceInFrontOfStaff')).toBe(true); + expect(stylesheet.raw.get('Global/TuningSpaceInFrontOfStaff') as number).toBeCloseTo(2.0, 3); - expect(stylesheet.raw.has('StandardNotation/drawWholeRestOnEmptyBars')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/drawWholeRestOnEmptyBars')).to.equal(false); + expect(stylesheet.raw.has('StandardNotation/drawWholeRestOnEmptyBars')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/drawWholeRestOnEmptyBars')).toBe(false); - expect(stylesheet.raw.has('Global/miniBrowserPosition')).to.be.true; - expect(stylesheet.raw.get('Global/miniBrowserPosition')).to.equal(0); + expect(stylesheet.raw.has('Global/miniBrowserPosition')).toBe(true); + expect(stylesheet.raw.get('Global/miniBrowserPosition')).toBe(0); - expect(stylesheet.raw.has('StandardNotation/hideUselessRests')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/hideUselessRests')).to.equal(true); + expect(stylesheet.raw.has('StandardNotation/hideUselessRests')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/hideUselessRests')).toBe(true); - expect(stylesheet.raw.has('Global/SpacingAffectFontsSize')).to.be.true; - expect(stylesheet.raw.get('Global/SpacingAffectFontsSize')).to.equal(true); + expect(stylesheet.raw.has('Global/SpacingAffectFontsSize')).toBe(true); + expect(stylesheet.raw.get('Global/SpacingAffectFontsSize')).toBe(true); - expect(stylesheet.raw.has('Even/drawEvenCopyright')).to.be.true; - expect(stylesheet.raw.get('Even/drawEvenCopyright')).to.equal(true); + expect(stylesheet.raw.has('Even/drawEvenCopyright')).toBe(true); + expect(stylesheet.raw.get('Even/drawEvenCopyright')).toBe(true); - expect(stylesheet.raw.has('Global/RepeatTargetPriority')).to.be.true; - expect(stylesheet.raw.get('Global/RepeatTargetPriority')).to.equal(3300); + expect(stylesheet.raw.has('Global/RepeatTargetPriority')).toBe(true); + expect(stylesheet.raw.get('Global/RepeatTargetPriority')).toBe(3300); - expect(stylesheet.raw.has('Global/SVGFont')).to.be.true; - expect(stylesheet.raw.get('Global/SVGFont')).to.equal(':/renderer/resources/notes.svg'); + expect(stylesheet.raw.has('Global/SVGFont')).toBe(true); + expect(stylesheet.raw.get('Global/SVGFont')).toBe(':/renderer/resources/notes.svg'); - expect(stylesheet.raw.has('Footer/PageNumberAlignment')).to.be.true; - expect(stylesheet.raw.get('Footer/PageNumberAlignment')).to.equal(2); + expect(stylesheet.raw.has('Footer/PageNumberAlignment')).toBe(true); + expect(stylesheet.raw.get('Footer/PageNumberAlignment')).toBe(2); - expect(stylesheet.raw.has('Global/graceFlatScaleFactor')).to.be.true; - expect(stylesheet.raw.get('Global/graceFlatScaleFactor') as number).to.be.closeTo(0.58333, 0.0001); + expect(stylesheet.raw.has('Global/graceFlatScaleFactor')).toBe(true); + expect(stylesheet.raw.get('Global/graceFlatScaleFactor') as number).toBeCloseTo(0.58333, 3); - expect(stylesheet.raw.has('Global/shadowColorEnd')).to.be.true; - expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).r).to.equal(90); - expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).g).to.equal(90); - expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).b).to.equal(90); - expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).a).to.equal(10); + expect(stylesheet.raw.has('Global/shadowColorEnd')).toBe(true); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).r).toBe(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).g).toBe(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).b).toBe(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).a).toBe(10); - expect(stylesheet.raw.has('Even/EvenCopyright')).to.be.true; - expect(stylesheet.raw.get('Even/EvenCopyright')).to.equal('%COPYRIGHT%'); + expect(stylesheet.raw.has('Even/EvenCopyright')).toBe(true); + expect(stylesheet.raw.get('Even/EvenCopyright')).toBe('%COPYRIGHT%'); - expect(stylesheet.raw.has('Global/GolpePriority')).to.be.true; - expect(stylesheet.raw.get('Global/GolpePriority')).to.equal(350); + expect(stylesheet.raw.has('Global/GolpePriority')).toBe(true); + expect(stylesheet.raw.get('Global/GolpePriority')).toBe(350); - expect(stylesheet.raw.has('Global/spaceSizeMM')).to.be.true; - expect(stylesheet.raw.get('Global/spaceSizeMM') as number).to.be.closeTo(1.5, 0.0001); + expect(stylesheet.raw.has('Global/spaceSizeMM')).toBe(true); + expect(stylesheet.raw.get('Global/spaceSizeMM') as number).toBeCloseTo(1.5, 3); - expect(stylesheet.raw.has('TablatureNotation/drawSecondNoteTrill')).to.be.true; - expect(stylesheet.raw.get('TablatureNotation/drawSecondNoteTrill')).to.equal(true); + expect(stylesheet.raw.has('TablatureNotation/drawSecondNoteTrill')).toBe(true); + expect(stylesheet.raw.get('TablatureNotation/drawSecondNoteTrill')).toBe(true); - expect(stylesheet.raw.has('System/insertSize')).to.be.true; - expect(stylesheet.raw.get('System/insertSize')).to.equal(2); + expect(stylesheet.raw.has('System/insertSize')).toBe(true); + expect(stylesheet.raw.get('System/insertSize')).toBe(2); - expect(stylesheet.raw.has('TablatureNotation/minimalInformationForHarmonic')).to.be.true; - expect(stylesheet.raw.get('TablatureNotation/minimalInformationForHarmonic')).to.equal(true); + expect(stylesheet.raw.has('TablatureNotation/minimalInformationForHarmonic')).toBe(true); + expect(stylesheet.raw.get('TablatureNotation/minimalInformationForHarmonic')).toBe(true); - expect(stylesheet.raw.has('PageSetup/pageTopMargin')).to.be.true; - expect(stylesheet.raw.get('PageSetup/pageTopMargin') as number).to.be.closeTo(15, 0.0001); + expect(stylesheet.raw.has('PageSetup/pageTopMargin')).toBe(true); + expect(stylesheet.raw.get('PageSetup/pageTopMargin') as number).toBeCloseTo(15, 3); - expect(stylesheet.raw.has('StandardNotation/augmentationDotRadius')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/augmentationDotRadius') as number).to.be.closeTo(0.25, 0.0001); + expect(stylesheet.raw.has('StandardNotation/augmentationDotRadius')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/augmentationDotRadius') as number).toBeCloseTo(0.25, 3); - expect(stylesheet.raw.has('Odd/drawOddCopyright')).to.be.true; - expect(stylesheet.raw.get('Odd/drawOddCopyright')).to.equal(false); + expect(stylesheet.raw.has('Odd/drawOddCopyright')).toBe(true); + expect(stylesheet.raw.get('Odd/drawOddCopyright')).toBe(false); - expect(stylesheet.raw.has('TablatureNotation/forceRhythmicBand')).to.be.true; - expect(stylesheet.raw.get('TablatureNotation/forceRhythmicBand')).to.equal(false); + expect(stylesheet.raw.has('TablatureNotation/forceRhythmicBand')).toBe(true); + expect(stylesheet.raw.get('TablatureNotation/forceRhythmicBand')).toBe(false); - expect(stylesheet.raw.has('System/codaSplit')).to.be.true; - expect(stylesheet.raw.get('System/codaSplit')).to.equal(true); + expect(stylesheet.raw.has('System/codaSplit')).toBe(true); + expect(stylesheet.raw.get('System/codaSplit')).toBe(true); - expect(stylesheet.raw.has('StandardNotation/tieMaxHeight')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/tieMaxHeight') as number).to.be.closeTo(2.5, 0.0001); + expect(stylesheet.raw.has('StandardNotation/tieMaxHeight')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/tieMaxHeight') as number).toBeCloseTo(2.5, 3); - expect(stylesheet.raw.has('Header/WordsAndMusicAlignment')).to.be.true; - expect(stylesheet.raw.get('Header/WordsAndMusicAlignment')).to.equal(2); + expect(stylesheet.raw.has('Header/WordsAndMusicAlignment')).toBe(true); + expect(stylesheet.raw.get('Header/WordsAndMusicAlignment')).toBe(2); - expect(stylesheet.raw.has('Even/drawEvenFooter')).to.be.true; - expect(stylesheet.raw.get('Even/drawEvenFooter')).to.equal(true); + expect(stylesheet.raw.has('Even/drawEvenFooter')).toBe(true); + expect(stylesheet.raw.get('Even/drawEvenFooter')).toBe(true); - expect(stylesheet.raw.has('StandardNotation/rightFingeringPositionSN')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/rightFingeringPositionSN')).to.equal(1); + expect(stylesheet.raw.has('StandardNotation/rightFingeringPositionSN')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/rightFingeringPositionSN')).toBe(1); - expect(stylesheet.raw.has('System/bracketCurveHeight')).to.be.true; - expect(stylesheet.raw.get('System/bracketCurveHeight') as number).to.be.closeTo(0.8, 0.0001); + expect(stylesheet.raw.has('System/bracketCurveHeight')).toBe(true); + expect(stylesheet.raw.get('System/bracketCurveHeight') as number).toBeCloseTo(0.8, 3); - expect(stylesheet.raw.has('Global/FreeTimePriority')).to.be.true; - expect(stylesheet.raw.get('Global/FreeTimePriority')).to.equal(2700); + expect(stylesheet.raw.has('Global/FreeTimePriority')).toBe(true); + expect(stylesheet.raw.get('Global/FreeTimePriority')).toBe(2700); - expect(stylesheet.raw.has('Global/ChordSpacingMillimeter')).to.be.true; - expect(stylesheet.raw.get('Global/ChordSpacingMillimeter') as number).to.be.closeTo(3.0, 0.0001); + expect(stylesheet.raw.has('Global/ChordSpacingMillimeter')).toBe(true); + expect(stylesheet.raw.get('Global/ChordSpacingMillimeter') as number).toBeCloseTo(3.0, 3); - expect(stylesheet.raw.has('Header/drawAlbum')).to.be.true; - expect(stylesheet.raw.get('Header/drawAlbum')).to.equal(true); + expect(stylesheet.raw.has('Header/drawAlbum')).toBe(true); + expect(stylesheet.raw.get('Header/drawAlbum')).toBe(true); - expect(stylesheet.raw.has('System/trackNameModeMulti')).to.be.true; - expect(stylesheet.raw.get('System/trackNameModeMulti')).to.equal(1); + expect(stylesheet.raw.has('System/trackNameModeMulti')).toBe(true); + expect(stylesheet.raw.get('System/trackNameModeMulti')).toBe(1); - expect(stylesheet.raw.has('System/insertSizeSameTrack')).to.be.true; - expect(stylesheet.raw.get('System/insertSizeSameTrack')).to.equal(1); + expect(stylesheet.raw.has('System/insertSizeSameTrack')).toBe(true); + expect(stylesheet.raw.get('System/insertSizeSameTrack')).toBe(1); - expect(stylesheet.raw.has('System/marginMinimalBeforeFirstNote')).to.be.true; - expect(stylesheet.raw.get('System/marginMinimalBeforeFirstNote') as number).to.be.closeTo(1.5, 0.0001); + expect(stylesheet.raw.has('System/marginMinimalBeforeFirstNote')).toBe(true); + expect(stylesheet.raw.get('System/marginMinimalBeforeFirstNote') as number).toBeCloseTo(1.5, 3); - expect(stylesheet.raw.has('Header/Subtitle')).to.be.true; - expect(stylesheet.raw.get('Header/Subtitle')).to.equal('%SUBTITLE%'); + expect(stylesheet.raw.has('Header/Subtitle')).toBe(true); + expect(stylesheet.raw.get('Header/Subtitle')).toBe('%SUBTITLE%'); - expect(stylesheet.raw.has('Global/alphaSuggested')).to.be.true; - expect(stylesheet.raw.get('Global/alphaSuggested') as number).to.be.closeTo(0.5, 0.0001); + expect(stylesheet.raw.has('Global/alphaSuggested')).toBe(true); + expect(stylesheet.raw.get('Global/alphaSuggested') as number).toBeCloseTo(0.5, 3); - expect(stylesheet.raw.has('Even/EvenHeaderAlignment')).to.be.true; - expect(stylesheet.raw.get('Even/EvenHeaderAlignment')).to.equal(0); + expect(stylesheet.raw.has('Even/EvenHeaderAlignment')).toBe(true); + expect(stylesheet.raw.get('Even/EvenHeaderAlignment')).toBe(0); - expect(stylesheet.raw.has('Global/TechniqueSymbol')).to.be.true; - expect(stylesheet.raw.get('Global/TechniqueSymbol')).to.equal(25); + expect(stylesheet.raw.has('Global/TechniqueSymbol')).toBe(true); + expect(stylesheet.raw.get('Global/TechniqueSymbol')).toBe(25); - expect(stylesheet.raw.has('Global/tuningBoxed')).to.be.true; - expect(stylesheet.raw.get('Global/tuningBoxed')).to.equal(false); + expect(stylesheet.raw.has('Global/tuningBoxed')).toBe(true); + expect(stylesheet.raw.get('Global/tuningBoxed')).toBe(false); - expect(stylesheet.raw.has('StandardNotation/drawBends')).to.be.true; - expect(stylesheet.raw.get('StandardNotation/drawBends')).to.equal(true); + expect(stylesheet.raw.has('StandardNotation/drawBends')).toBe(true); + expect(stylesheet.raw.get('StandardNotation/drawBends')).toBe(true); - expect(stylesheet.raw.has('Global/mouseClickMaxTime')).to.be.true; - expect(stylesheet.raw.get('Global/mouseClickMaxTime')).to.equal(200); + expect(stylesheet.raw.has('Global/mouseClickMaxTime')).toBe(true); + expect(stylesheet.raw.get('Global/mouseClickMaxTime')).toBe(200); - expect(stylesheet.raw.has('Global/graceSharpScaleFactor')).to.be.true; - expect(stylesheet.raw.get('Global/graceSharpScaleFactor') as number).to.be.closeTo(0.6666, 0.0001); + expect(stylesheet.raw.has('Global/graceSharpScaleFactor')).toBe(true); + expect(stylesheet.raw.get('Global/graceSharpScaleFactor') as number).toBeCloseTo(0.6666, 3); - expect(stylesheet.raw.has('Global/GrayedOpacity')).to.be.true; - expect(stylesheet.raw.get('Global/GrayedOpacity') as number).to.be.closeTo(0.2, 0.0001); + expect(stylesheet.raw.has('Global/GrayedOpacity')).toBe(true); + expect(stylesheet.raw.get('Global/GrayedOpacity') as number).toBeCloseTo(0.2, 3); - expect(stylesheet.raw.has('Global/WhammyBarVibratoPriority')).to.be.true; - expect(stylesheet.raw.get('Global/WhammyBarVibratoPriority')).to.equal(1400); + expect(stylesheet.raw.has('Global/WhammyBarVibratoPriority')).toBe(true); + expect(stylesheet.raw.get('Global/WhammyBarVibratoPriority')).toBe(1400); - expect(stylesheet.raw.has('TablatureNotation/noStaffLineForSlashs')).to.be.true; - expect(stylesheet.raw.get('TablatureNotation/noStaffLineForSlashs')).to.equal(false); + expect(stylesheet.raw.has('TablatureNotation/noStaffLineForSlashs')).toBe(true); + expect(stylesheet.raw.get('TablatureNotation/noStaffLineForSlashs')).toBe(false); }); }); diff --git a/packages/alphatab/test/importer/Gp3Importer.test.ts b/packages/alphatab/test/importer/Gp3Importer.test.ts index 61e84c68e..519e9707f 100644 --- a/packages/alphatab/test/importer/Gp3Importer.test.ts +++ b/packages/alphatab/test/importer/Gp3Importer.test.ts @@ -1,30 +1,29 @@ import { AutomationType } from '@coderline/alphatab/model/Automation'; import { BrushType } from '@coderline/alphatab/model/BrushType'; import { DynamicValue } from '@coderline/alphatab/model/DynamicValue'; +import { HarmonicType } from '@coderline/alphatab/model/HarmonicType'; import type { Score } from '@coderline/alphatab/model/Score'; import { SlideOutType } from '@coderline/alphatab/model/SlideOutType'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; -import { HarmonicType } from '@coderline/alphatab/model/HarmonicType'; -import { expect } from 'chai'; - +import { describe, expect, it } from 'vitest'; describe('Gp3ImporterTest', () => { it('score-info', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/score-info.gp3'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Title'); - expect(score.subTitle).to.equal('Subtitle'); - expect(score.artist).to.equal('Artist'); - expect(score.album).to.equal('Album'); - expect(score.words).to.equal('Music'); // no words in gp4 + expect(score.title).toBe('Title'); + expect(score.subTitle).toBe('Subtitle'); + expect(score.artist).toBe('Artist'); + expect(score.album).toBe('Album'); + expect(score.words).toBe('Music'); // no words in gp4 - expect(score.music).to.equal('Music'); - expect(score.copyright).to.equal('Copyright'); - expect(score.tab).to.equal('Tab'); - expect(score.instructions).to.equal('Instructions'); - expect(score.notices).to.equal('Notice1\r\nNotice2'); - expect(score.masterBars.length).to.equal(5); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].name).to.equal('Track 1'); + expect(score.music).toBe('Music'); + expect(score.copyright).toBe('Copyright'); + expect(score.tab).toBe('Tab'); + expect(score.instructions).toBe('Instructions'); + expect(score.notices).toBe('Notice1\r\nNotice2'); + expect(score.masterBars.length).toBe(5); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].name).toBe('Track 1'); }); it('notes', async () => { @@ -48,28 +47,28 @@ describe('Gp3ImporterTest', () => { it('accentuations', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/accentuations.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).toBe(true); // it seems accentuation is handled as Forte Fortissimo - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].dynamics).to.equal(DynamicValue.FFF); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].dynamics).toBe(DynamicValue.FFF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).toBe(true); }); it('harmonics', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/harmonics.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).to.be.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toBe( HarmonicType.Natural ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).to.be.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).to.be.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).to.be.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).to.be.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toBe( HarmonicType.Artificial ); }); @@ -89,10 +88,10 @@ describe('Gp3ImporterTest', () => { it('slides', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/slides.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).toBe( SlideOutType.Shift ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).toBe( SlideOutType.Shift ); }); @@ -106,28 +105,28 @@ describe('Gp3ImporterTest', () => { it('other-effects', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/other-effects.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).to.equal('C'); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).to.equal('Text'); - expect(score.tracks[0].staves[0].bars[4].masterBar.tempoAutomations.length).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).toBe('C'); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).toBe('Text'); + expect(score.tracks[0].staves[0].bars[4].masterBar.tempoAutomations.length).toBe(1); expect( score.tracks[0].staves[0].bars[4].masterBar.tempoAutomations[0]!.value - ).to.equal(120); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)).to.be.ok; + ).toBe(120); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)).toBeTruthy(); expect( score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)!.value - ).to.equal(25); + ).toBe(25); }); it('strokes', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/strokes.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).to.equal(BrushType.BrushDown); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).to.equal(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).toBe(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).toBe(BrushType.BrushUp); }); it('tuplets', async () => { @@ -139,10 +138,10 @@ describe('Gp3ImporterTest', () => { it('ranges', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/ranges.gp3'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).toBe(true); }); it('effects', async () => { @@ -156,4 +155,20 @@ describe('Gp3ImporterTest', () => { const score: Score = reader.readScore(); GpImporterTestHelper.checkStrings(score); }); + + it('beat-harmonics', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/beat-harmonics.gp3'); + const score = reader.readScore(); + const b0 = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + const b1 = score.tracks[0].staves[0].bars[0].voices[0].beats[1]; + expect(b0.notes[0].harmonicType).toBe(HarmonicType.Natural); + expect(b0.notes[0].harmonicValue).toBe(12); + expect(b0.notes[1].harmonicType).toBe(HarmonicType.Natural); + expect(b0.notes[1].harmonicValue).toBe(12); + expect(b1.notes[0].harmonicType).toBe(HarmonicType.Artificial); + expect(b1.notes[0].harmonicValue).toBe(12); + expect(b1.notes[1].harmonicType).toBe(HarmonicType.Artificial); + expect(b1.notes[1].harmonicValue).toBe(12); + }); }); + \ No newline at end of file diff --git a/packages/alphatab/test/importer/Gp4Importer.test.ts b/packages/alphatab/test/importer/Gp4Importer.test.ts index 325ee1d3a..37e89fc30 100644 --- a/packages/alphatab/test/importer/Gp4Importer.test.ts +++ b/packages/alphatab/test/importer/Gp4Importer.test.ts @@ -1,25 +1,25 @@ +import { HarmonicType } from '@coderline/alphatab/model/HarmonicType'; import type { Score } from '@coderline/alphatab/model/Score'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; -import { expect } from 'chai'; - +import { describe, expect, it } from 'vitest'; describe('Gp4ImporterTest', () => { it('score-info', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/score-info.gp4'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Title'); - expect(score.subTitle).to.equal('Subtitle'); - expect(score.artist).to.equal('Artist'); - expect(score.album).to.equal('Album'); - expect(score.words).to.equal('Music'); // no words in gp4 + expect(score.title).toBe('Title'); + expect(score.subTitle).toBe('Subtitle'); + expect(score.artist).toBe('Artist'); + expect(score.album).toBe('Album'); + expect(score.words).toBe('Music'); // no words in gp4 - expect(score.music).to.equal('Music'); - expect(score.copyright).to.equal('Copyright'); - expect(score.tab).to.equal('Tab'); - expect(score.instructions).to.equal('Instructions'); - expect(score.notices).to.equal('Notice1\r\nNotice2'); - expect(score.masterBars.length).to.equal(5); - expect(score.tracks.length).to.equal(1); - expect(score.tracks[0].name).to.equal('Track 1'); + expect(score.music).toBe('Music'); + expect(score.copyright).toBe('Copyright'); + expect(score.tab).toBe('Tab'); + expect(score.instructions).toBe('Instructions'); + expect(score.notices).toBe('Notice1\r\nNotice2'); + expect(score.masterBars.length).toBe(5); + expect(score.tracks.length).toBe(1); + expect(score.tracks[0].name).toBe('Track 1'); }); it('notes', async () => { @@ -141,4 +141,36 @@ describe('Gp4ImporterTest', () => { const score: Score = reader.readScore(); GpImporterTestHelper.checkColors(score); }); + + it('harmonic-types', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/harmonic-types.gp4'); + const score = reader.readScore(); + const b0 = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + expect(b0.notes[0].harmonicType).toBe(HarmonicType.Natural); + expect(b0.notes[0].harmonicValue).toBe(12); + + const b1 = score.tracks[0].staves[0].bars[0].voices[0].beats[1]; + expect(b1.notes[0].harmonicType).toBe(HarmonicType.Artificial); + expect(b1.notes[0].harmonicValue).toBe(5); + + const b2 = score.tracks[0].staves[0].bars[0].voices[0].beats[2]; + expect(b2.notes[0].harmonicType).toBe(HarmonicType.Artificial); + expect(b2.notes[0].harmonicValue).toBe(7); + + const b3 = score.tracks[0].staves[0].bars[0].voices[0].beats[3]; + expect(b3.notes[0].harmonicType).toBe(HarmonicType.Artificial); + expect(b3.notes[0].harmonicValue).toBe(12); + + const b4 = score.tracks[0].staves[0].bars[1].voices[0].beats[0]; + expect(b4.notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(b4.notes[0].harmonicValue).toBe(12); + + const b5 = score.tracks[0].staves[0].bars[1].voices[0].beats[1]; + expect(b5.notes[0].harmonicType).toBe(HarmonicType.Pinch); + expect(b5.notes[0].harmonicValue).toBe(12); + + const b6 = score.tracks[0].staves[0].bars[1].voices[0].beats[2]; + expect(b6.notes[0].harmonicType).toBe(HarmonicType.Semi); + expect(b6.notes[0].harmonicValue).toBe(12); + }); }); diff --git a/packages/alphatab/test/importer/Gp5Importer.test.ts b/packages/alphatab/test/importer/Gp5Importer.test.ts index 4ef957e89..4c1625790 100644 --- a/packages/alphatab/test/importer/Gp5Importer.test.ts +++ b/packages/alphatab/test/importer/Gp5Importer.test.ts @@ -1,34 +1,39 @@ import { Settings } from '@coderline/alphatab/Settings'; +import { GpBinaryHelpers } from '@coderline/alphatab/importer/Gp3To5Importer'; +import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; +import { EndOfReaderError, OverflowError } from '@coderline/alphatab/io/IReadable'; import { type Beat, BeatBeamingMode } from '@coderline/alphatab/model/Beat'; +import { Clef } from '@coderline/alphatab/model/Clef'; import { Direction } from '@coderline/alphatab/model/Direction'; +import { HarmonicType } from '@coderline/alphatab/model/HarmonicType'; import { Ottavia } from '@coderline/alphatab/model/Ottavia'; +import { PercussionMapper } from '@coderline/alphatab/model/PercussionMapper'; import { type Score, ScoreSubElement } from '@coderline/alphatab/model/Score'; import { WahPedal } from '@coderline/alphatab/model/WahPedal'; import { TextAlign } from '@coderline/alphatab/platform/ICanvas'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; +import { TestPlatform } from 'test/TestPlatform'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; -import { expect } from 'chai'; -import { Clef } from '@coderline/alphatab/model/Clef'; -import { PercussionMapper } from '@coderline/alphatab/model/PercussionMapper'; +import { describe, expect, it } from 'vitest'; describe('Gp5ImporterTest', () => { it('score-info', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/score-info.gp5'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Title'); - expect(score.subTitle).to.equal('Subtitle'); - expect(score.artist).to.equal('Artist'); - expect(score.album).to.equal('Album'); - expect(score.words).to.equal('Words'); - expect(score.music).to.equal('Music'); - expect(score.copyright).to.equal('Copyright'); - expect(score.tab).to.equal('Tab'); - expect(score.instructions).to.equal('Instructions'); - expect(score.notices).to.equal('Notice1\r\nNotice2'); - expect(score.masterBars.length).to.equal(5); - expect(score.tracks.length).to.equal(2); - expect(score.tracks[0].name).to.equal('Track 1'); - expect(score.tracks[1].name).to.equal('Track 2'); + expect(score.title).toBe('Title'); + expect(score.subTitle).toBe('Subtitle'); + expect(score.artist).toBe('Artist'); + expect(score.album).toBe('Album'); + expect(score.words).toBe('Words'); + expect(score.music).toBe('Music'); + expect(score.copyright).toBe('Copyright'); + expect(score.tab).toBe('Tab'); + expect(score.instructions).toBe('Instructions'); + expect(score.notices).toBe('Notice1\r\nNotice2'); + expect(score.masterBars.length).toBe(5); + expect(score.tracks.length).toBe(2); + expect(score.tracks[0].name).toBe('Track 1'); + expect(score.tracks[1].name).toBe('Track 2'); }); it('notes', async () => { @@ -174,36 +179,36 @@ describe('Gp5ImporterTest', () => { 'guitarpro5/alternate-endings-section-error.gp5' ); const score: Score = reader.readScore(); - expect(score.masterBars.length).to.be.equal(2); - expect(score.masterBars[1].alternateEndings).to.be.equal(4); - expect(score.masterBars[1].section).to.be.ok; - expect(score.masterBars[1].section?.text).to.be.equal('Outro'); + expect(score.masterBars.length).toBe(2); + expect(score.masterBars[1].alternateEndings).toBe(4); + expect(score.masterBars[1].section).toBeTruthy(); + expect(score.masterBars[1].section?.text).toBe('Outro'); }); it('canon', async () => { const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/canon.gp5'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Canon Rock'); - expect(score.subTitle).to.equal(''); - expect(score.artist).to.equal('JerryC'); - expect(score.album).to.equal(''); - expect(score.words).to.equal(''); - expect(score.music).to.equal('JerryC'); - expect(score.copyright).to.equal(''); - expect(score.tab).to.equal(''); - expect(score.instructions).to.equal(''); - expect(score.notices).to.equal(''); - expect(score.masterBars.length).to.equal(224); - expect(score.tracks.length).to.equal(9); - expect(score.tracks[0].name).to.equal('Guitar Player'); - expect(score.tracks[1].name).to.equal('Low Bassy Sound'); - expect(score.tracks[2].name).to.equal('High Soundy Thing'); - expect(score.tracks[3].name).to.equal('Second Guitar'); - expect(score.tracks[4].name).to.equal('Drums'); - expect(score.tracks[5].name).to.equal('Harmonizer'); - expect(score.tracks[6].name).to.equal('The clean guitar'); - expect(score.tracks[7].name).to.equal('Track 8'); - expect(score.tracks[8].name).to.equal('Percussion'); + expect(score.title).toBe('Canon Rock'); + expect(score.subTitle).toBe(''); + expect(score.artist).toBe('JerryC'); + expect(score.album).toBe(''); + expect(score.words).toBe(''); + expect(score.music).toBe('JerryC'); + expect(score.copyright).toBe(''); + expect(score.tab).toBe(''); + expect(score.instructions).toBe(''); + expect(score.notices).toBe(''); + expect(score.masterBars.length).toBe(224); + expect(score.tracks.length).toBe(9); + expect(score.tracks[0].name).toBe('Guitar Player'); + expect(score.tracks[1].name).toBe('Low Bassy Sound'); + expect(score.tracks[2].name).toBe('High Soundy Thing'); + expect(score.tracks[3].name).toBe('Second Guitar'); + expect(score.tracks[4].name).toBe('Drums'); + expect(score.tracks[5].name).toBe('Harmonizer'); + expect(score.tracks[6].name).toBe('The clean guitar'); + expect(score.tracks[7].name).toBe('Track 8'); + expect(score.tracks[8].name).toBe('Percussion'); }); it('beat-text-lyrics', async () => { const settings = new Settings(); @@ -272,7 +277,7 @@ describe('Gp5ImporterTest', () => { beat = beat.nextBeat; } - expect(actualChunks.join(';')).to.equal(expectedChunks.join(';')); + expect(actualChunks.join(';')).toBe(expectedChunks.join(';')); }); it('layout-configuration', async () => { @@ -295,42 +300,42 @@ describe('Gp5ImporterTest', () => { it('hide-tuning', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/hide-tuning.gp5')).readScore(); - expect(score.stylesheet.perTrackDisplayTuning).to.be.ok; - expect(score.stylesheet.perTrackDisplayTuning!.has(0)).to.be.true; - expect(score.stylesheet.perTrackDisplayTuning!.get(0)).to.equal(false); + expect(score.stylesheet.perTrackDisplayTuning).toBeTruthy(); + expect(score.stylesheet.perTrackDisplayTuning!.has(0)).toBe(true); + expect(score.stylesheet.perTrackDisplayTuning!.get(0)).toBe(false); - expect(score.stylesheet.perTrackDisplayTuning!.has(1)).to.be.true; - expect(score.stylesheet.perTrackDisplayTuning!.get(1)).to.equal(true); + expect(score.stylesheet.perTrackDisplayTuning!.has(1)).toBe(true); + expect(score.stylesheet.perTrackDisplayTuning!.get(1)).toBe(true); }); it('staves', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/staves.gp5')).readScore(); - expect(score.tracks[0].staves[0].showNumbered).to.be.false; - expect(score.tracks[0].staves[0].showSlash).to.be.false; - expect(score.tracks[0].staves[0].showTablature).to.be.true; - expect(score.tracks[0].staves[0].showStandardNotation).to.be.true; + expect(score.tracks[0].staves[0].showNumbered).toBe(false); + expect(score.tracks[0].staves[0].showSlash).toBe(false); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); - expect(score.tracks[1].staves[0].showNumbered).to.be.false; - expect(score.tracks[1].staves[0].showSlash).to.be.false; - expect(score.tracks[1].staves[0].showTablature).to.be.false; - expect(score.tracks[1].staves[0].showStandardNotation).to.be.true; + expect(score.tracks[1].staves[0].showNumbered).toBe(false); + expect(score.tracks[1].staves[0].showSlash).toBe(false); + expect(score.tracks[1].staves[0].showTablature).toBe(false); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); - expect(score.tracks[2].staves[0].showNumbered).to.be.false; - expect(score.tracks[2].staves[0].showSlash).to.be.false; - expect(score.tracks[2].staves[0].showTablature).to.be.true; - expect(score.tracks[2].staves[0].showStandardNotation).to.be.false; + expect(score.tracks[2].staves[0].showNumbered).toBe(false); + expect(score.tracks[2].staves[0].showSlash).toBe(false); + expect(score.tracks[2].staves[0].showTablature).toBe(true); + expect(score.tracks[2].staves[0].showStandardNotation).toBe(false); }); it('hide-diagram', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/hide-diagrams.gp5')).readScore(); - expect(score.stylesheet.perTrackChordDiagramsOnTop).to.be.ok; - expect(score.stylesheet.perTrackChordDiagramsOnTop!.has(0)).to.be.true; - expect(score.stylesheet.perTrackChordDiagramsOnTop!.get(0)).to.equal(false); + expect(score.stylesheet.perTrackChordDiagramsOnTop).toBeTruthy(); + expect(score.stylesheet.perTrackChordDiagramsOnTop!.has(0)).toBe(true); + expect(score.stylesheet.perTrackChordDiagramsOnTop!.get(0)).toBe(false); - expect(score.stylesheet.perTrackChordDiagramsOnTop!.has(1)).to.be.true; - expect(score.stylesheet.perTrackChordDiagramsOnTop!.get(1)).to.equal(true); + expect(score.stylesheet.perTrackChordDiagramsOnTop!.has(1)).toBe(true); + expect(score.stylesheet.perTrackChordDiagramsOnTop!.get(1)).toBe(true); }); it('directions', async () => { @@ -366,8 +371,8 @@ describe('Gp5ImporterTest', () => { ]; for (let i = 0; i < expectedDirections.length; i++) { - expect(score.masterBars[i].directions).to.be.ok; - expect(score.masterBars[i].directions!.has(expectedDirections[i])).to.be.true; + expect(score.masterBars[i].directions).toBeTruthy(); + expect(score.masterBars[i].directions!.has(expectedDirections[i])).toBe(true); } }); @@ -375,184 +380,174 @@ describe('Gp5ImporterTest', () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/beaming-mode.gp5')).readScore(); // auto - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).not.toBeTruthy(); // force - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.Auto // already has beam, no need to force ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).toBe( BeatBeamingMode.Auto // already has beam, no need to force ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).not.toBeTruthy(); // break - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).to.equal( - BeatBeamingMode.ForceSplitToNext - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.ForceSplitToNext); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.Auto // already has no beam, no need to break ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).to.equal( - BeatBeamingMode.ForceSplitToNext - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).toBe(BeatBeamingMode.ForceSplitToNext); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).not.toBeTruthy(); // break secondary - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitOnSecondaryToNext ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).not.toBeTruthy(); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).to.not.be.ok; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).not.toBeTruthy(); // invert to down - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).to.equal( - BeamDirection.Down - ); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Down); // invert to up - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); }); it('ottavia', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/ottavia.gp5')).readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].ottava).to.equal(Ottavia._8va); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].ottava).to.equal(Ottavia._8vb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].ottava).to.equal(Ottavia._15ma); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].ottava).to.equal(Ottavia._15mb); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].ottava).to.equal(Ottavia.Regular); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].ottava).toBe(Ottavia._8va); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].ottava).toBe(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].ottava).toBe(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].ottava).toBe(Ottavia._15mb); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].ottava).toBe(Ottavia.Regular); }); it('wah-wah', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/wah-wah.gp5')).readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].wahPedal).to.equal(WahPedal.None); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].wahPedal).to.equal(WahPedal.Open); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].wahPedal).to.equal(WahPedal.Closed); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].wahPedal).to.equal(WahPedal.Open); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].wahPedal).to.equal(WahPedal.Closed); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].wahPedal).to.equal(WahPedal.None); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].wahPedal).toBe(WahPedal.None); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].wahPedal).toBe(WahPedal.Open); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].wahPedal).toBe(WahPedal.Closed); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].wahPedal).toBe(WahPedal.Open); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].wahPedal).toBe(WahPedal.Closed); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].wahPedal).toBe(WahPedal.None); }); it('header-footer', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/header-footer.gp5')).readScore(); - expect(score.style).to.be.ok; + expect(score.style).toBeTruthy(); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).to.equal('Title: %TITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).toBe('Title: %TITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).to.equal('Subtitle: %SUBTITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).toBe('Subtitle: %SUBTITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).to.equal('Artist: %ARTIST%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).toBe('Artist: %ARTIST%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).to.equal('Album: %ALBUM%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).toBe('Album: %ALBUM%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).to.equal('Words: %WORDS%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).toBe('Words: %WORDS%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).to.equal('Music: %MUSIC%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).toBe('Music: %MUSIC%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).toBe( 'Words & Music: %WORDSMUSIC%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).to.be.false; + expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).toBe(false); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).to.equal( - 'Copyright: %COPYRIGHT%' - ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).to.equal(TextAlign.Center); - - expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).to.equal('Copyright2'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).to.equal( - TextAlign.Center - ); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).toBe('Copyright: %COPYRIGHT%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).toBe(TextAlign.Center); + + expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).toBe('Copyright2'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).toBe(TextAlign.Center); }); it('bank', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/bank.gp5')).readScore(); - expect(score.tracks[0].playbackInfo.program).to.equal(25); - expect(score.tracks[0].playbackInfo.bank).to.equal(0); + expect(score.tracks[0].playbackInfo.program).toBe(25); + expect(score.tracks[0].playbackInfo.bank).toBe(0); - expect(score.tracks[1].playbackInfo.program).to.equal(25); - expect(score.tracks[1].playbackInfo.bank).to.equal(77); + expect(score.tracks[1].playbackInfo.program).toBe(25); + expect(score.tracks[1].playbackInfo.bank).toBe(77); }); it('tuning-bass-clef', async () => { const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/bass-tuning.gp5')).readScore(); - expect(score.tracks[0].staves[0].bars[0].clef).to.equal(Clef.G2); - expect(score.tracks[1].staves[0].bars[0].clef).to.equal(Clef.F4); - expect(score.tracks[2].staves[0].bars[0].clef).to.equal(Clef.F4); - expect(score.tracks[3].staves[0].bars[0].clef).to.equal(Clef.F4); + expect(score.tracks[0].staves[0].bars[0].clef).toBe(Clef.G2); + expect(score.tracks[1].staves[0].bars[0].clef).toBe(Clef.F4); + expect(score.tracks[2].staves[0].bars[0].clef).toBe(Clef.F4); + expect(score.tracks[3].staves[0].bars[0].clef).toBe(Clef.F4); }); it('percusson', async () => { @@ -564,9 +559,109 @@ describe('Gp5ImporterTest', () => { if (beat.notes.length === 1) { const articulationName = PercussionMapper.getArticulationName(beat.notes[0]); const hasArticulation = PercussionMapper.instrumentArticulationNames.has(articulationName); - expect(hasArticulation).to.be.true; + expect(hasArticulation).toBe(true); beat = beat.nextBeat; } } }); + + it('chord-name-overflow', async () => { + // GP5 file with a chord name length byte that exceeds the 21-byte field + // (length=32). Pre-fix, gpReadStringByteLength consumed the full 32 bytes, + // mis-aligning the stream and triggering an unbounded readBend loop. + const score = ( + await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/chord-name-overflow.gp5') + ).readScore(); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(193); + }); + + it('gpReadStringByteLength caps consumption at field width', () => { + const sentinelByte = 0xca; + const fieldSize = 21; + const overlongHint = 32; + + const raw = new Uint8Array(fieldSize + 2); + raw[0] = overlongHint; + for (let i = 0; i < fieldSize; i++) { + raw[i + 1] = 0x41; + } + raw[fieldSize + 1] = sentinelByte; + + const buffer = ByteBuffer.fromBuffer(raw); + + const result = GpBinaryHelpers.gpReadStringByteLength(buffer, fieldSize, 'utf-8'); + expect(result).toBe('A'.repeat(fieldSize)); + expect(buffer.position).toBe(1 + fieldSize); + expect(buffer.readByte()).toBe(sentinelByte); + }); + + describe('corrupt', () => { + async function corruptTest(intToWrite: number, offset: number, expectedOverflowLabel: string) { + const buffer = await TestPlatform.loadFile(`test-data/corrupt/healthy.gp5`); + + buffer[offset + 0] = (intToWrite >> 0) & 0xff; + buffer[offset + 1] = (intToWrite >> 8) & 0xff; + buffer[offset + 2] = (intToWrite >> 16) & 0xff; + buffer[offset + 3] = (intToWrite >> 24) & 0xff; + + const importer = GpImporterTestHelper.prepareImporterWithBytes(buffer, new Settings()); + + try { + importer.readScore(); + throw new Error('Expected readScore to fail with an OverflowError'); + } catch (e) { + if (e instanceof OverflowError) { + expect((e as OverflowError).message).toContain(expectedOverflowLabel); + return; + } + throw e; + } + } + + it('max-bar-count', async () => await corruptTest(5000, 1235, 'bar count')); + + it('max-track-count', async () => await corruptTest(300, 1239, 'track count')); + + it('notice-lines-count', async () => await corruptTest(5000, 82, 'notice line count')); + + it('beat-count', async () => await corruptTest(200, 1460, 'beat count')); + + it('tremolo-count', async () => await corruptTest(500, 1584, 'tremolo bar point count')); + + it('bend-count', async () => await corruptTest(500, 1479, 'note bend point count')); + it('eof', async () => { + let buffer = await TestPlatform.loadFile(`test-data/corrupt/healthy.gp5`); + + buffer = buffer.slice(0, buffer.length / 2); + + const importer = GpImporterTestHelper.prepareImporterWithBytes(buffer, new Settings()); + + expect(() => importer.readScore()).toThrow(EndOfReaderError); + }); + }); + + it('harmonic-types', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/harmonic-types.gp5'); + const score = reader.readScore(); + const b0 = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + expect(b0.notes[0].harmonicType).toBe(HarmonicType.Natural); + expect(b0.notes[0].harmonicValue).toBe(12); + + const b1 = score.tracks[0].staves[0].bars[0].voices[0].beats[1]; + expect(b1.notes[0].harmonicType).toBe(HarmonicType.Artificial); + expect(b1.notes[0].harmonicValue).toBe(17); + + const b2 = score.tracks[0].staves[0].bars[0].voices[0].beats[2]; + expect(b2.notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(b2.notes[0].harmonicValue).toBe(12); + + const b3 = score.tracks[0].staves[0].bars[0].voices[0].beats[3]; + expect(b3.notes[0].harmonicType).toBe(HarmonicType.Pinch); + expect(b3.notes[0].harmonicValue).toBe(12); + + const b4 = score.tracks[0].staves[0].bars[1].voices[0].beats[0]; + expect(b4.notes[0].harmonicType).toBe(HarmonicType.Semi); + expect(b4.notes[0].harmonicValue).toBe(12); + }); }); diff --git a/packages/alphatab/test/importer/Gp7Importer.test.ts b/packages/alphatab/test/importer/Gp7Importer.test.ts index beac718d4..e81669f19 100644 --- a/packages/alphatab/test/importer/Gp7Importer.test.ts +++ b/packages/alphatab/test/importer/Gp7Importer.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { MidiUtils } from '@coderline/alphatab/midi/MidiUtils'; import { Gp7To8Importer } from '@coderline/alphatab/importer/Gp7To8Importer'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; @@ -17,7 +18,6 @@ import { Settings } from '@coderline/alphatab/Settings'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; import { TestPlatform } from 'test/TestPlatform'; import { AutomationType } from '@coderline/alphatab/model/Automation'; -import { expect } from 'chai'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; describe('Gp7ImporterTest', () => { @@ -35,20 +35,20 @@ describe('Gp7ImporterTest', () => { it('score-info', async () => { const reader = await prepareImporterWithFile('guitarpro7/score-info.gp'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Title'); - expect(score.subTitle).to.equal('Subtitle'); - expect(score.artist).to.equal('Artist'); - expect(score.album).to.equal('Album'); - expect(score.words).to.equal('Words'); - expect(score.music).to.equal('Music'); - expect(score.copyright).to.equal('Copyright'); - expect(score.tab).to.equal('Tab'); - expect(score.instructions).to.equal('Instructions'); - expect(score.notices).to.equal('Notice1\nNotice2'); - expect(score.masterBars.length).to.equal(5); - expect(score.tracks.length).to.equal(2); - expect(score.tracks[0].name).to.equal('Track 1'); - expect(score.tracks[1].name).to.equal('Track 2'); + expect(score.title).toBe('Title'); + expect(score.subTitle).toBe('Subtitle'); + expect(score.artist).toBe('Artist'); + expect(score.album).toBe('Album'); + expect(score.words).toBe('Words'); + expect(score.music).toBe('Music'); + expect(score.copyright).toBe('Copyright'); + expect(score.tab).toBe('Tab'); + expect(score.instructions).toBe('Instructions'); + expect(score.notices).toBe('Notice1\nNotice2'); + expect(score.masterBars.length).toBe(5); + expect(score.tracks.length).toBe(2); + expect(score.tracks[0].name).toBe('Track 1'); + expect(score.tracks[1].name).toBe('Track 2'); }); it('notes', async () => { @@ -96,28 +96,28 @@ describe('Gp7ImporterTest', () => { it('bend', async () => { const reader = await prepareImporterWithFile('guitarpro7/bends.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).to.equal(BendType.Bend); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).to.equal(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendType).to.equal(BendType.Bend); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendType).to.equal(BendType.BendRelease); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).to.equal(30); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).to.equal(12); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).to.equal(30); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).to.equal(12); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].value).to.equal(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).toBe(BendType.Bend); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toBe(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendType).toBe(BendType.Bend); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendType).toBe(BendType.BendRelease); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).toBe(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).toBe(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).toBe(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).toBe(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).toBe(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].value).toBe(6); }); it('bends-advanced', async () => { @@ -128,293 +128,293 @@ describe('Gp7ImporterTest', () => { // // Bar 1 let note: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(10.2, 0.001); - expect(note.bendPoints![1].value).to.equal(4); - expect(note.bendPoints![2].offset).to.be.closeTo(20.4, 0.001); - expect(note.bendPoints![2].value).to.equal(4); - expect(note.bendPoints![3].offset).to.be.closeTo(30, 0.001); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2, 3); + expect(note.bendPoints![1].value).toBe(4); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4, 3); + expect(note.bendPoints![2].value).toBe(4); + expect(note.bendPoints![3].offset).toBeCloseTo(30, 3); + expect(note.bendPoints![3].value).toBe(0); // // Bar 2 note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(59.4, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(59.4, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(10.2, 0.001); - expect(note.bendPoints![1].value).to.equal(4); - expect(note.bendPoints![2].offset).to.be.closeTo(45.6, 0.001); - expect(note.bendPoints![2].value).to.equal(4); - expect(note.bendPoints![3].offset).to.be.closeTo(59.4, 0.001); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2, 3); + expect(note.bendPoints![1].value).toBe(4); + expect(note.bendPoints![2].offset).toBeCloseTo(45.6, 3); + expect(note.bendPoints![2].value).toBe(4); + expect(note.bendPoints![3].offset).toBeCloseTo(59.4, 3); + expect(note.bendPoints![3].value).toBe(0); // // Bar 3 note = score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Prebend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(60, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Prebend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendBend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(6); + expect(note.bendType).toBe(BendType.PrebendBend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(6); // // Bar 4 note = score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendRelease); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(0); + expect(note.bendType).toBe(BendType.PrebendRelease); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(0); // // Bar 5 note = score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(14.4, 0.001); - expect(note.bendPoints![1].value).to.equal(8); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(14.4, 3); + expect(note.bendPoints![1].value).toBe(8); note = score.tracks[0].staves[0].bars[4].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(9, 0.001); - expect(note.bendPoints![1].value).to.equal(8); - expect(note.bendPoints![2].offset).to.be.closeTo(20.4, 0.001); - expect(note.bendPoints![2].value).to.equal(8); - expect(note.bendPoints![3].offset).to.be.closeTo(31.2, 0.001); - expect(note.bendPoints![3].value).to.equal(4); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(9, 3); + expect(note.bendPoints![1].value).toBe(8); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4, 3); + expect(note.bendPoints![2].value).toBe(8); + expect(note.bendPoints![3].offset).toBeCloseTo(31.2, 3); + expect(note.bendPoints![3].value).toBe(4); // // Bar 6 note = score.tracks[0].staves[0].bars[5].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Prebend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(8); - expect(note.bendPoints![1].offset).to.be.closeTo(60, 0.001); - expect(note.bendPoints![1].value).to.equal(8); + expect(note.bendType).toBe(BendType.Prebend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(8); + expect(note.bendPoints![1].offset).toBeCloseTo(60, 3); + expect(note.bendPoints![1].value).toBe(8); note = score.tracks[0].staves[0].bars[5].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendBend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(8); - expect(note.bendPoints![1].offset).to.be.closeTo(16.2, 0.001); - expect(note.bendPoints![1].value).to.equal(12); + expect(note.bendType).toBe(BendType.PrebendBend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(8); + expect(note.bendPoints![1].offset).toBeCloseTo(16.2, 3); + expect(note.bendPoints![1].value).toBe(12); // // Bar 7 note = score.tracks[0].staves[0].bars[6].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendRelease); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(8); - expect(note.bendPoints![1].offset).to.be.closeTo(14.4, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.PrebendRelease); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(8); + expect(note.bendPoints![1].offset).toBeCloseTo(14.4, 3); + expect(note.bendPoints![1].value).toBe(4); // // Bar 8 note = score.tracks[0].staves[0].bars[7].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); // // Bar 9 note = score.tracks[0].staves[0].bars[8].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(10.2, 0.001); - expect(note.bendPoints![1].value).to.equal(4); - expect(note.bendPoints![2].offset).to.be.closeTo(20.4, 0.001); - expect(note.bendPoints![2].value).to.equal(4); - expect(note.bendPoints![3].offset).to.be.closeTo(30, 0.001); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2, 3); + expect(note.bendPoints![1].value).toBe(4); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4, 3); + expect(note.bendPoints![2].value).toBe(4); + expect(note.bendPoints![3].offset).toBeCloseTo(30, 3); + expect(note.bendPoints![3].value).toBe(0); // Combined Bends // // Bar 10 note = score.tracks[0].staves[0].bars[9].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[9].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.Release); - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(0); + expect(note.bendType).toBe(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(0); note = score.tracks[0].staves[0].bars[9].voices[0].beats[2].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.isContinuedBend).to.be.equal(false); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.isContinuedBend).toBe(false); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); // // Bar 11 note = score.tracks[0].staves[0].bars[10].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[10].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(8); + expect(note.bendType).toBe(BendType.Bend); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(8); note = score.tracks[0].staves[0].bars[10].voices[0].beats[2].notes[0]; - expect(note.bendType).to.equal(BendType.Release); - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(8); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(8); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[10].voices[0].beats[3].notes[0]; - expect(note.bendType).to.equal(BendType.Release); - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(0); + expect(note.bendType).toBe(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(0); // Grace Bends // // Bar 12 note = score.tracks[0].staves[0].bars[11].voices[0].beats[0].notes[0]; - expect(note.beat.graceType).to.equal(GraceType.BeforeBeat); - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.beat.graceType).toBe(GraceType.BeforeBeat); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); // // Bar 13 note = score.tracks[0].staves[0].bars[12].voices[0].beats[0].notes[0]; - expect(note.beat.graceType).to.equal(GraceType.BeforeBeat); - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.beat.graceType).toBe(GraceType.BeforeBeat); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[12].voices[0].beats[1].notes[0]; - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendType).to.equal(BendType.Hold); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(60, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toBe(BendType.Hold); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60, 3); + expect(note.bendPoints![1].value).toBe(4); // // Bar 14 note = score.tracks[0].staves[0].bars[13].voices[0].beats[0].notes[0]; - expect(note.beat.graceType).to.equal(GraceType.OnBeat); - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(18, 0.001); - expect(note.bendPoints![1].value).to.equal(1); + expect(note.beat.graceType).toBe(GraceType.OnBeat); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(18, 3); + expect(note.bendPoints![1].value).toBe(1); note = score.tracks[0].staves[0].bars[13].voices[0].beats[1].notes[0]; - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendType).to.equal(BendType.Hold); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(1); - expect(note.bendPoints![1].offset).to.be.closeTo(60, 0.001); - expect(note.bendPoints![1].value).to.equal(1); + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toBe(BendType.Hold); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(1); + expect(note.bendPoints![1].offset).toBeCloseTo(60, 3); + expect(note.bendPoints![1].value).toBe(1); // // Bar 15 note = score.tracks[0].staves[0].bars[14].voices[0].beats[0].notes[0]; - expect(note.beat.graceType).to.equal(GraceType.BeforeBeat); - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.beat.graceType).toBe(GraceType.BeforeBeat); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[0]; - expect(note.fret).to.equal(12); - expect(note.isTieDestination).to.be.equal(true); - expect(note.isContinuedBend).to.be.equal(true); - expect(note.bendType).to.equal(BendType.Hold); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.be.closeTo(60, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.fret).toBe(12); + expect(note.isTieDestination).toBe(true); + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toBe(BendType.Hold); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60, 3); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[1]; - expect(note.fret).to.equal(10); - expect(note.isContinuedBend).to.be.equal(false); - expect(note.hasBend).to.be.equal(false); - expect(note.bendType).to.equal(BendType.None); + expect(note.fret).toBe(10); + expect(note.isContinuedBend).toBe(false); + expect(note.hasBend).toBe(false); + expect(note.bendType).toBe(BendType.None); note = score.tracks[0].staves[0].bars[15].voices[0].beats[0].notes[0]; - expect(note.fret).to.equal(10); - expect(note.bendType).to.equal(BendType.None); + expect(note.fret).toBe(10); + expect(note.bendType).toBe(BendType.None); // // Bar 16 note = score.tracks[0].staves[0].bars[15].voices[0].beats[0].notes[1]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.be.closeTo(0, 0.001); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.be.closeTo(15, 0.001); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0, 3); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15, 3); + expect(note.bendPoints![1].value).toBe(4); }); it('whammy-advanced', async () => { @@ -423,235 +423,208 @@ describe('Gp7ImporterTest', () => { // Bar 1 let beat: Beat = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(45, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-4); + expect(beat.whammyBarType).toBe(WhammyType.Dive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45, 3); + expect(beat.whammyBarPoints![1].value).toBe(-4); beat = score.tracks[0].staves[0].bars[0].voices[0].beats[2]; - expect(beat.whammyBarType).to.equal(WhammyType.PrediveDive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-4); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(60, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-16); + expect(beat.whammyBarType).toBe(WhammyType.PrediveDive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60, 3); + expect(beat.whammyBarPoints![1].value).toBe(-16); // Bar 2 beat = score.tracks[0].staves[0].bars[1].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(3); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(15, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-16); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(30, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(beat.whammyBarPoints![1].value).toBe(-16); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30, 3); + expect(beat.whammyBarPoints![2].value).toBe(0); beat = score.tracks[0].staves[0].bars[1].voices[0].beats[2]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(4); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(14.4, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-12); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(31.8, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(-12); - expect(beat.whammyBarPoints![3].offset).to.be.closeTo(53.4, 0.001); - expect(beat.whammyBarPoints![3].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(4); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(14.4, 3); + expect(beat.whammyBarPoints![1].value).toBe(-12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(31.8, 3); + expect(beat.whammyBarPoints![2].value).toBe(-12); + expect(beat.whammyBarPoints![3].offset).toBeCloseTo(53.4, 3); + expect(beat.whammyBarPoints![3].value).toBe(0); // Bar 3 beat = score.tracks[0].staves[0].bars[2].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(3); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(15, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-16); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(30, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(beat.whammyBarPoints![1].value).toBe(-16); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30, 3); + expect(beat.whammyBarPoints![2].value).toBe(0); beat = score.tracks[0].staves[0].bars[2].voices[0].beats[2]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(4); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(14.4, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-12); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(31.8, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(-12); - expect(beat.whammyBarPoints![3].offset).to.be.closeTo(53.4, 0.001); - expect(beat.whammyBarPoints![3].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(4); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(14.4, 3); + expect(beat.whammyBarPoints![1].value).toBe(-12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(31.8, 3); + expect(beat.whammyBarPoints![2].value).toBe(-12); + expect(beat.whammyBarPoints![3].offset).toBeCloseTo(53.4, 3); + expect(beat.whammyBarPoints![3].value).toBe(0); // Bar 4 beat = score.tracks[0].staves[0].bars[3].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Predive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-8); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(60, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-8); + expect(beat.whammyBarType).toBe(WhammyType.Predive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-8); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60, 3); + expect(beat.whammyBarPoints![1].value).toBe(-8); // Bar 5 beat = score.tracks[0].staves[0].bars[4].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.PrediveDive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-4); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(30, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.PrediveDive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(30, 3); + expect(beat.whammyBarPoints![1].value).toBe(0); // Bar 6 beat = score.tracks[0].staves[0].bars[5].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.PrediveDive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-4); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(29.4, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-12); + expect(beat.whammyBarType).toBe(WhammyType.PrediveDive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(29.4, 3); + expect(beat.whammyBarPoints![1].value).toBe(-12); beat = score.tracks[0].staves[0].bars[5].voices[0].beats[1]; - expect(beat.whammyBarType).to.equal(WhammyType.Dive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-12); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(45.6, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-12); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45.6, 3); + expect(beat.whammyBarPoints![1].value).toBe(0); // Bar 7 beat = score.tracks[0].staves[0].bars[6].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(45, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-4); + expect(beat.whammyBarType).toBe(WhammyType.Dive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45, 3); + expect(beat.whammyBarPoints![1].value).toBe(-4); beat = score.tracks[0].staves[0].bars[6].voices[0].beats[1]; - expect(beat.whammyBarType).to.equal(WhammyType.Hold); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-4); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(60, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-4); + expect(beat.whammyBarType).toBe(WhammyType.Hold); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60, 3); + expect(beat.whammyBarPoints![1].value).toBe(-4); // Bar 8 beat = score.tracks[0].staves[0].bars[7].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-4); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(46.2, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-12); + expect(beat.whammyBarType).toBe(WhammyType.Dive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(46.2, 3); + expect(beat.whammyBarPoints![1].value).toBe(-12); beat = score.tracks[0].staves[0].bars[7].voices[0].beats[1]; - expect(beat.whammyBarType).to.equal(WhammyType.Dive); - expect(beat.whammyBarPoints!.length).to.equal(2); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(-12); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(44.4, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(8); + expect(beat.whammyBarType).toBe(WhammyType.Dive); + expect(beat.whammyBarPoints!.length).toBe(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(-12); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(44.4, 3); + expect(beat.whammyBarPoints![1].value).toBe(8); // Bar 9 beat = score.tracks[0].staves[0].bars[8].voices[0].beats[0]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(3); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(8); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(15, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(12); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(30, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(8); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(beat.whammyBarPoints![1].value).toBe(12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30, 3); + expect(beat.whammyBarPoints![2].value).toBe(0); beat = score.tracks[0].staves[0].bars[8].voices[0].beats[1]; - expect(beat.whammyBarType).to.equal(WhammyType.Dip); - expect(beat.whammyBarPoints!.length).to.equal(3); - expect(beat.whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(beat.whammyBarPoints![0].value).to.equal(0); - expect(beat.whammyBarPoints![1].offset).to.be.closeTo(15, 0.001); - expect(beat.whammyBarPoints![1].value).to.equal(-4); - expect(beat.whammyBarPoints![2].offset).to.be.closeTo(30, 0.001); - expect(beat.whammyBarPoints![2].value).to.equal(0); + expect(beat.whammyBarType).toBe(WhammyType.Dip); + expect(beat.whammyBarPoints!.length).toBe(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(beat.whammyBarPoints![0].value).toBe(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(beat.whammyBarPoints![1].value).toBe(-4); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30, 3); + expect(beat.whammyBarPoints![2].value).toBe(0); }); it('tremolo', async () => { const reader = await prepareImporterWithFile('guitarpro7/tremolo.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toBe(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toBe(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toBe(2); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toBe(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).to.equal(4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toBe(4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).to.equal(4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).toBe(4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 15, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).toBe(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 30.6, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).to.equal(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30.6, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).toBe(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).to.be.closeTo( - 45, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(45, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).toBe(0); }); it('slides', async () => { @@ -741,62 +714,62 @@ describe('Gp7ImporterTest', () => { it('tremolo-vibrato', async () => { const reader = await prepareImporterWithFile('guitarpro7/tremolo-vibrato.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].vibrato).to.equal(VibratoType.Wide); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[1].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).to.equal(VibratoType.Wide); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).to.equal(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].vibrato).toBe(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[1].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).toBe(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).toBe(VibratoType.Wide); }); it('ottavia', async () => { const reader = await prepareImporterWithFile('guitarpro7/ottavia.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].clefOttava).to.equal(Ottavia._8va); - expect(score.tracks[0].staves[0].bars[1].clefOttava).to.equal(Ottavia._8vb); - expect(score.tracks[0].staves[0].bars[2].clefOttava).to.equal(Ottavia._15ma); - expect(score.tracks[0].staves[0].bars[3].clefOttava).to.equal(Ottavia._15mb); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].ottava).to.equal(Ottavia._8va); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].ottava).to.equal(Ottavia._8vb); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[2].ottava).to.equal(Ottavia._15ma); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[3].ottava).to.equal(Ottavia._15mb); + expect(score.tracks[0].staves[0].bars[0].clefOttava).toBe(Ottavia._8va); + expect(score.tracks[0].staves[0].bars[1].clefOttava).toBe(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[2].clefOttava).toBe(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[3].clefOttava).toBe(Ottavia._15mb); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].ottava).toBe(Ottavia._8va); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].ottava).toBe(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[2].ottava).toBe(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[3].ottava).toBe(Ottavia._15mb); }); it('simile-mark', async () => { const reader = await prepareImporterWithFile('guitarpro7/simile-mark.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].simileMark).to.equal(SimileMark.None); - expect(score.tracks[0].staves[0].bars[1].simileMark).to.equal(SimileMark.Simple); - expect(score.tracks[0].staves[0].bars[2].simileMark).to.equal(SimileMark.None); - expect(score.tracks[0].staves[0].bars[3].simileMark).to.equal(SimileMark.None); - expect(score.tracks[0].staves[0].bars[4].simileMark).to.equal(SimileMark.FirstOfDouble); - expect(score.tracks[0].staves[0].bars[5].simileMark).to.equal(SimileMark.SecondOfDouble); + expect(score.tracks[0].staves[0].bars[0].simileMark).toBe(SimileMark.None); + expect(score.tracks[0].staves[0].bars[1].simileMark).toBe(SimileMark.Simple); + expect(score.tracks[0].staves[0].bars[2].simileMark).toBe(SimileMark.None); + expect(score.tracks[0].staves[0].bars[3].simileMark).toBe(SimileMark.None); + expect(score.tracks[0].staves[0].bars[4].simileMark).toBe(SimileMark.FirstOfDouble); + expect(score.tracks[0].staves[0].bars[5].simileMark).toBe(SimileMark.SecondOfDouble); }); it('anacrusis', async () => { const reader = await prepareImporterWithFile('guitarpro7/anacrusis.gp'); const score: Score = reader.readScore(); - expect(score.masterBars[0].isAnacrusis).to.be.equal(true); - expect(score.masterBars[0].calculateDuration()).to.equal(1920); - expect(score.masterBars[1].calculateDuration()).to.equal(3840); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toBe(1920); + expect(score.masterBars[1].calculateDuration()).toBe(3840); }); it('left-hand-tap', async () => { const reader = await prepareImporterWithFile('guitarpro7/left-hand-tap.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLeftHandTapped).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].isLeftHandTapped).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[6].notes[0].isLeftHandTapped).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[9].notes[0].isLeftHandTapped).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[6].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[9].notes[0].isLeftHandTapped).toBe(true); }); it('fermata', async () => { const reader = await prepareImporterWithFile('guitarpro7/fermata.gp'); const score: Score = reader.readScore(); - expect(score.masterBars[0].fermata!.size).to.equal(5); - expect(score.masterBars[1].fermata!.size).to.equal(5); - expect(score.masterBars[2].fermata!.size).to.equal(5); // Short + expect(score.masterBars[0].fermata!.size).toBe(5); + expect(score.masterBars[1].fermata!.size).toBe(5); + expect(score.masterBars[2].fermata!.size).toBe(5); // Short const offsets = [ 0, (MidiUtils.QuarterTime * (1 / 2)) | 0, @@ -807,20 +780,20 @@ describe('Gp7ImporterTest', () => { const types: FermataType[] = [FermataType.Short, FermataType.Medium, FermataType.Long]; for (let i: number = 0; i < 3; i++) { const masterBar: MasterBar = score.masterBars[i]; - expect(masterBar.fermata!.size).to.equal(5); + expect(masterBar.fermata!.size).toBe(5); for (const offset of offsets) { const fermata = masterBar.fermata!.get(offset); - expect(fermata).to.be.ok; - expect(fermata!.type).to.equal(types[i]); + expect(fermata).toBeTruthy(); + expect(fermata!.type).toBe(types[i]); } const beats: Beat[] = score.tracks[0].staves[0].bars[i].voices[0].beats; for (const beat of beats) { const fermata = masterBar.fermata!.get(beat.playbackStart); const beatFermata = beat.fermata; - expect(beatFermata).to.be.ok; - expect(fermata).to.be.ok; - expect(beatFermata!.type).to.equal(types[i]); - expect(fermata!.type).to.equal(types[i]); + expect(beatFermata).toBeTruthy(); + expect(fermata).toBeTruthy(); + expect(beatFermata!.type).toBe(types[i]); + expect(fermata!.type).toBe(types[i]); } } }); @@ -829,112 +802,112 @@ describe('Gp7ImporterTest', () => { const reader = await prepareImporterWithFile('guitarpro7/pick-slide.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideUp ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideUp ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].fret).to.equal(20); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].fret).toBe(20); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].fret).to.equal(12); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].fret).toBe(12); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].fret).to.equal(0); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].fret).toBe(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].fret).to.equal(20); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].fret).toBe(20); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].fret).to.equal(12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].fret).toBe(12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].slideOutType).toBe( SlideOutType.PickSlideUp ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].fret).toBe(5); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].slideOutType).toBe( SlideOutType.PickSlideUp ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].fret).to.equal(10); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].fret).toBe(10); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].slideOutType).toBe( SlideOutType.PickSlideDown ); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].fret).to.equal(20); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].fret).toBe(20); }); it('beat-lyrics', async () => { const reader = await prepareImporterWithFile('guitarpro7/beat-lyrics.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].lyrics![0]).to.be.equal('This'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics![0]).to.be.equal('is'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).to.be.equal('a'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![0]).to.be.equal('test file'); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].lyrics![0]).to.be.equal('for'); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].lyrics![0]).to.be.equal('lyrics'); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].lyrics).to.be.equal(null); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].lyrics).to.be.equal(null); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].lyrics![0]).toBe('This'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].lyrics![0]).toBe('is'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics![0]).toBe('a'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].lyrics![0]).toBe('test file'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].lyrics![0]).toBe('for'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].lyrics![0]).toBe('lyrics'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].lyrics).toBe(null); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].lyrics).toBe(null); }); it('track-volume', async () => { const reader = await prepareImporterWithFile('guitarpro7/track-volume.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].playbackInfo.volume).to.be.equal(16); - expect(score.tracks[1].playbackInfo.volume).to.be.equal(14); - expect(score.tracks[2].playbackInfo.volume).to.be.equal(12); - expect(score.tracks[3].playbackInfo.volume).to.be.equal(10); - expect(score.tracks[4].playbackInfo.volume).to.be.equal(7); - expect(score.tracks[5].playbackInfo.volume).to.be.equal(3); - expect(score.tracks[6].playbackInfo.volume).to.be.equal(0); + expect(score.tracks[0].playbackInfo.volume).toBe(16); + expect(score.tracks[1].playbackInfo.volume).toBe(14); + expect(score.tracks[2].playbackInfo.volume).toBe(12); + expect(score.tracks[3].playbackInfo.volume).toBe(10); + expect(score.tracks[4].playbackInfo.volume).toBe(7); + expect(score.tracks[5].playbackInfo.volume).toBe(3); + expect(score.tracks[6].playbackInfo.volume).toBe(0); }); it('track-balance', async () => { const reader = await prepareImporterWithFile('guitarpro7/track-balance.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].playbackInfo.balance).to.be.equal(0); - expect(score.tracks[1].playbackInfo.balance).to.be.equal(4); - expect(score.tracks[2].playbackInfo.balance).to.be.equal(8); - expect(score.tracks[3].playbackInfo.balance).to.be.equal(12); - expect(score.tracks[4].playbackInfo.balance).to.be.equal(16); + expect(score.tracks[0].playbackInfo.balance).toBe(0); + expect(score.tracks[1].playbackInfo.balance).toBe(4); + expect(score.tracks[2].playbackInfo.balance).toBe(8); + expect(score.tracks[3].playbackInfo.balance).toBe(12); + expect(score.tracks[4].playbackInfo.balance).toBe(16); }); it('program-change', async () => { const reader = await prepareImporterWithFile('guitarpro7/program-change.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].playbackInfo.program).to.be.equal(25); + expect(score.tracks[0].playbackInfo.program).toBe(25); const automation = score.tracks[0].staves[0].bars[2].voices[0].beats[0].getAutomation( AutomationType.Instrument ); - expect(automation).to.be.ok; + expect(automation).toBeTruthy(); if (automation) { - expect(automation.value).to.be.equal(29); + expect(automation.value).toBe(29); } }); @@ -942,9 +915,9 @@ describe('Gp7ImporterTest', () => { const reader = await prepareImporterWithFile('guitarpro7/chord-no-diagram.gp'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).to.be.equal('C'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings.length).to.be.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).toBe('C'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings.length).toBe(0); }); it('layout-configuration', async () => { @@ -969,85 +942,85 @@ describe('Gp7ImporterTest', () => { const score = (await prepareImporterWithFile('guitarpro7/beaming-mode.gp')).readScore(); // auto - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); // force - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).toBe(BeamDirection.Up); // break - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).toBe(BeamDirection.Up); // break secondary - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitOnSecondaryToNext ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); // invert to down - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).to.equal( + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).toBe( BeamDirection.Down ); // invert to up - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); }); }); diff --git a/packages/alphatab/test/importer/Gp8Importer.test.ts b/packages/alphatab/test/importer/Gp8Importer.test.ts index 0ef7ffa1d..15b8d7c0f 100644 --- a/packages/alphatab/test/importer/Gp8Importer.test.ts +++ b/packages/alphatab/test/importer/Gp8Importer.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { Gp7To8Importer } from '@coderline/alphatab/importer/Gp7To8Importer'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { AutomationType } from '@coderline/alphatab/model/Automation'; @@ -16,7 +17,6 @@ import { TextAlign } from '@coderline/alphatab/platform/ICanvas'; import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; import { Settings } from '@coderline/alphatab/Settings'; import { SynthConstants } from '@coderline/alphatab/synth/SynthConstants'; -import { expect } from 'chai'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; import { TestPlatform } from 'test/TestPlatform'; @@ -53,318 +53,318 @@ describe('Gp8ImporterTest', () => { it('beat-tempo-change', async () => { const score = (await prepareImporterWithFile('guitarpro8/beat-tempo-change.gp')).readScore(); - expect(score.masterBars[0].tempoAutomations.length).to.equal(2); - expect(score.masterBars[0].tempoAutomations[0].value).to.have.equal(120); - expect(score.masterBars[0].tempoAutomations[0].ratioPosition).to.equal(0); - expect(score.masterBars[0].tempoAutomations[1].value).to.equal(60); - expect(score.masterBars[0].tempoAutomations[1].ratioPosition).to.equal(0.5); - - expect(score.masterBars[1].tempoAutomations.length).to.equal(2); - expect(score.masterBars[1].tempoAutomations[0].value).to.equal(100); - expect(score.masterBars[1].tempoAutomations[0].ratioPosition).to.equal(0); - expect(score.masterBars[1].tempoAutomations[1].value).to.equal(120); - expect(score.masterBars[1].tempoAutomations[1].ratioPosition).to.equal(0.6375); + expect(score.masterBars[0].tempoAutomations.length).toBe(2); + expect(score.masterBars[0].tempoAutomations[0].value).toBe(120); + expect(score.masterBars[0].tempoAutomations[0].ratioPosition).toBe(0); + expect(score.masterBars[0].tempoAutomations[1].value).toBe(60); + expect(score.masterBars[0].tempoAutomations[1].ratioPosition).toBe(0.5); + + expect(score.masterBars[1].tempoAutomations.length).toBe(2); + expect(score.masterBars[1].tempoAutomations[0].value).toBe(100); + expect(score.masterBars[1].tempoAutomations[0].ratioPosition).toBe(0); + expect(score.masterBars[1].tempoAutomations[1].value).toBe(120); + expect(score.masterBars[1].tempoAutomations[1].ratioPosition).toBe(0.6375); }); it('bracket-braces', async () => { const noBrackets = (await prepareImporterWithFile('visual-tests/layout/brackets-braces-none.gp')).readScore(); - expect(noBrackets.stylesheet.bracketExtendMode).to.equal(BracketExtendMode.NoBrackets); + expect(noBrackets.stylesheet.bracketExtendMode).toBe(BracketExtendMode.NoBrackets); const groupStaves = ( await prepareImporterWithFile('visual-tests/layout/brackets-braces-staves.gp') ).readScore(); - expect(groupStaves.stylesheet.bracketExtendMode).to.equal(BracketExtendMode.GroupStaves); + expect(groupStaves.stylesheet.bracketExtendMode).toBe(BracketExtendMode.GroupStaves); const groupSimilarInstruments = ( await prepareImporterWithFile('visual-tests/layout/brackets-braces-similar.gp') ).readScore(); - expect(groupSimilarInstruments.stylesheet.bracketExtendMode).to.equal( + expect(groupSimilarInstruments.stylesheet.bracketExtendMode).toBe( BracketExtendMode.GroupSimilarInstruments ); }); it('system-separator', async () => { const noBrackets = (await prepareImporterWithFile('visual-tests/layout/system-divider.gp')).readScore(); - expect(noBrackets.stylesheet.useSystemSignSeparator).to.be.true; + expect(noBrackets.stylesheet.useSystemSignSeparator).toBe(true); }); it('directions', async () => { const directions = (await prepareImporterWithFile('guitarpro8/directions.gp')).readScore(); - expect(directions.masterBars[0].directions).to.be.ok; - expect(directions.masterBars[0].directions).to.contain(Direction.TargetFine); - expect(directions.masterBars[0].directions).to.contain(Direction.TargetSegno); - expect(directions.masterBars[0].directions).to.contain(Direction.TargetSegnoSegno); - expect(directions.masterBars[0].directions).to.contain(Direction.TargetCoda); - expect(directions.masterBars[0].directions).to.contain(Direction.TargetDoubleCoda); - - expect(directions.masterBars[1]).to.be.ok; - expect(directions.masterBars[1].directions).to.contain(Direction.JumpDaCapo); - expect(directions.masterBars[1].directions).to.contain(Direction.JumpDalSegno); - expect(directions.masterBars[1].directions).to.contain(Direction.JumpDalSegnoSegno); - expect(directions.masterBars[1].directions).to.contain(Direction.JumpDaCoda); - expect(directions.masterBars[1].directions).to.contain(Direction.JumpDaDoubleCoda); - - expect(directions.masterBars[2].directions).to.be.ok; - expect(directions.masterBars[2].directions).to.contain(Direction.JumpDaCapoAlCoda); - expect(directions.masterBars[2].directions).to.contain(Direction.JumpDalSegnoAlCoda); - expect(directions.masterBars[2].directions).to.contain(Direction.JumpDalSegnoSegnoAlCoda); - - expect(directions.masterBars[3].directions).to.be.ok; - expect(directions.masterBars[3].directions).to.contain(Direction.JumpDaCapoAlDoubleCoda); - expect(directions.masterBars[3].directions).to.contain(Direction.JumpDalSegnoAlDoubleCoda); - expect(directions.masterBars[3].directions).to.contain(Direction.JumpDalSegnoSegnoAlDoubleCoda); - - expect(directions.masterBars[4].directions).to.be.ok; - expect(directions.masterBars[4].directions).to.contain(Direction.JumpDaCapoAlFine); - expect(directions.masterBars[4].directions).to.contain(Direction.JumpDalSegnoAlFine); - expect(directions.masterBars[4].directions).to.contain(Direction.JumpDalSegnoSegnoAlFine); - - expect(directions.masterBars[5].directions).to.not.be.ok; + expect(directions.masterBars[0].directions).toBeTruthy(); + expect(directions.masterBars[0].directions).toContain(Direction.TargetFine); + expect(directions.masterBars[0].directions).toContain(Direction.TargetSegno); + expect(directions.masterBars[0].directions).toContain(Direction.TargetSegnoSegno); + expect(directions.masterBars[0].directions).toContain(Direction.TargetCoda); + expect(directions.masterBars[0].directions).toContain(Direction.TargetDoubleCoda); + + expect(directions.masterBars[1]).toBeTruthy(); + expect(directions.masterBars[1].directions).toContain(Direction.JumpDaCapo); + expect(directions.masterBars[1].directions).toContain(Direction.JumpDalSegno); + expect(directions.masterBars[1].directions).toContain(Direction.JumpDalSegnoSegno); + expect(directions.masterBars[1].directions).toContain(Direction.JumpDaCoda); + expect(directions.masterBars[1].directions).toContain(Direction.JumpDaDoubleCoda); + + expect(directions.masterBars[2].directions).toBeTruthy(); + expect(directions.masterBars[2].directions).toContain(Direction.JumpDaCapoAlCoda); + expect(directions.masterBars[2].directions).toContain(Direction.JumpDalSegnoAlCoda); + expect(directions.masterBars[2].directions).toContain(Direction.JumpDalSegnoSegnoAlCoda); + + expect(directions.masterBars[3].directions).toBeTruthy(); + expect(directions.masterBars[3].directions).toContain(Direction.JumpDaCapoAlDoubleCoda); + expect(directions.masterBars[3].directions).toContain(Direction.JumpDalSegnoAlDoubleCoda); + expect(directions.masterBars[3].directions).toContain(Direction.JumpDalSegnoSegnoAlDoubleCoda); + + expect(directions.masterBars[4].directions).toBeTruthy(); + expect(directions.masterBars[4].directions).toContain(Direction.JumpDaCapoAlFine); + expect(directions.masterBars[4].directions).toContain(Direction.JumpDalSegnoAlFine); + expect(directions.masterBars[4].directions).toContain(Direction.JumpDalSegnoSegnoAlFine); + + expect(directions.masterBars[5].directions).not.toBeTruthy(); }); it('hide-tuning', async () => { const hide = (await prepareImporterWithFile('guitarpro8/hide-tuning.gp')).readScore(); - expect(hide.stylesheet.globalDisplayTuning).to.be.false; + expect(hide.stylesheet.globalDisplayTuning).toBe(false); const show = (await prepareImporterWithFile('guitarpro8/directions.gp')).readScore(); - expect(show.stylesheet.globalDisplayTuning).to.be.true; + expect(show.stylesheet.globalDisplayTuning).toBe(true); }); it('hide-chord-diagram-list', async () => { const hide = (await prepareImporterWithFile('guitarpro8/hide-diagrams.gp')).readScore(); - expect(hide.stylesheet.globalDisplayChordDiagramsOnTop).to.be.false; + expect(hide.stylesheet.globalDisplayChordDiagramsOnTop).toBe(false); const show = (await prepareImporterWithFile('guitarpro8/directions.gp')).readScore(); - expect(show.stylesheet.globalDisplayChordDiagramsOnTop).to.be.true; + expect(show.stylesheet.globalDisplayChordDiagramsOnTop).toBe(true); }); it('show-chord-diagrams-in-score', async () => { const hide = (await prepareImporterWithFile('guitarpro8/show-diagrams-in-score.gp')).readScore(); - expect(hide.stylesheet.globalDisplayChordDiagramsInScore).to.be.true; + expect(hide.stylesheet.globalDisplayChordDiagramsInScore).toBe(true); const show = (await prepareImporterWithFile('guitarpro8/directions.gp')).readScore(); - expect(show.stylesheet.globalDisplayChordDiagramsInScore).to.be.false; + expect(show.stylesheet.globalDisplayChordDiagramsInScore).toBe(false); }); it('beaming-mode', async () => { const score = (await prepareImporterWithFile('guitarpro8/beaming-mode.gp')).readScore(); // auto - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); // force - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].beamingMode).toBe( BeatBeamingMode.ForceMergeWithNext ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].preferredBeamDirection).toBe(BeamDirection.Up); // break - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].beamingMode).toBe( BeatBeamingMode.ForceSplitToNext ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].preferredBeamDirection).toBe(BeamDirection.Up); // break secondary - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).to.equal( + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].beamingMode).toBe( BeatBeamingMode.ForceSplitOnSecondaryToNext ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].preferredBeamDirection).toBe(BeamDirection.Up); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].preferredBeamDirection).toBe(BeamDirection.Up); // invert to down - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).to.equal( + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].preferredBeamDirection).toBe( BeamDirection.Down ); // invert to up - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).to.equal(BeatBeamingMode.Auto); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).to.be.false; - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).to.equal(BeamDirection.Up); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].beamingMode).toBe(BeatBeamingMode.Auto); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].invertBeamDirection).toBe(false); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].preferredBeamDirection).toBe(BeamDirection.Up); }); it('track-names-hidden', async () => { const hide = (await prepareImporterWithFile('guitarpro8/track-names-hidden.gp')).readScore(); - expect(hide.stylesheet.singleTrackTrackNamePolicy).to.equal(TrackNamePolicy.Hidden); - expect(hide.stylesheet.multiTrackTrackNamePolicy).to.equal(TrackNamePolicy.Hidden); + expect(hide.stylesheet.singleTrackTrackNamePolicy).toBe(TrackNamePolicy.Hidden); + expect(hide.stylesheet.multiTrackTrackNamePolicy).toBe(TrackNamePolicy.Hidden); }); it('track-names-adjusted', async () => { const hide = (await prepareImporterWithFile('guitarpro8/track-names.gp')).readScore(); - expect(hide.stylesheet.singleTrackTrackNamePolicy).to.equal(TrackNamePolicy.AllSystems); - expect(hide.stylesheet.multiTrackTrackNamePolicy).to.equal(TrackNamePolicy.AllSystems); + expect(hide.stylesheet.singleTrackTrackNamePolicy).toBe(TrackNamePolicy.AllSystems); + expect(hide.stylesheet.multiTrackTrackNamePolicy).toBe(TrackNamePolicy.AllSystems); - expect(hide.stylesheet.firstSystemTrackNameMode).to.equal(TrackNameMode.FullName); - expect(hide.stylesheet.otherSystemsTrackNameMode).to.equal(TrackNameMode.FullName); + expect(hide.stylesheet.firstSystemTrackNameMode).toBe(TrackNameMode.FullName); + expect(hide.stylesheet.otherSystemsTrackNameMode).toBe(TrackNameMode.FullName); - expect(hide.stylesheet.firstSystemTrackNameOrientation).to.equal(TrackNameOrientation.Horizontal); - expect(hide.stylesheet.otherSystemsTrackNameOrientation).to.equal(TrackNameOrientation.Vertical); + expect(hide.stylesheet.firstSystemTrackNameOrientation).toBe(TrackNameOrientation.Horizontal); + expect(hide.stylesheet.otherSystemsTrackNameOrientation).toBe(TrackNameOrientation.Vertical); }); it('timer', async () => { const score = (await prepareImporterWithFile('guitarpro8/timer.gp')).readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].timer).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].timer).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].timer).to.equal(2000); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].timer).toBe(2000); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].timer).to.equal(4000); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].timer).toBe(4000); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].timer).to.equal(6000); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].timer).toBe(6000); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].timer).to.equal(8000); - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].timer).toBe(8000); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].timer).to.equal(16000); - expect(score.tracks[0].staves[0].bars[5].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[0].timer).toBe(16000); + expect(score.tracks[0].staves[0].bars[5].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[6].voices[0].beats[0].showTimer).to.be.true; + expect(score.tracks[0].staves[0].bars[6].voices[0].beats[0].showTimer).toBe(true); // inprecision bug in guitar pro, should actually be 26000 - expect(score.tracks[0].staves[0].bars[6].voices[0].beats[0].timer).to.equal(25999); - expect(score.tracks[0].staves[0].bars[6].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[6].voices[0].beats[0].timer).toBe(25999); + expect(score.tracks[0].staves[0].bars[6].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[7].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[7].voices[0].beats[0].timer).to.equal(28000); - expect(score.tracks[0].staves[0].bars[7].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[7].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[7].voices[0].beats[0].timer).toBe(28000); + expect(score.tracks[0].staves[0].bars[7].voices[0].beats[1].showTimer).toBe(false); - expect(score.tracks[0].staves[0].bars[8].voices[0].beats[0].showTimer).to.be.true; - expect(score.tracks[0].staves[0].bars[8].voices[0].beats[0].timer).to.equal(0); - expect(score.tracks[0].staves[0].bars[8].voices[0].beats[1].showTimer).to.be.false; + expect(score.tracks[0].staves[0].bars[8].voices[0].beats[0].showTimer).toBe(true); + expect(score.tracks[0].staves[0].bars[8].voices[0].beats[0].timer).toBe(0); + expect(score.tracks[0].staves[0].bars[8].voices[0].beats[1].showTimer).toBe(false); }); it('multibar-rest', async () => { const enabled = (await prepareImporterWithFile('guitarpro8/multibar-rest.gp')).readScore(); const disabled = (await prepareImporterWithFile('guitarpro8/timer.gp')).readScore(); - expect(disabled.stylesheet.multiTrackMultiBarRest).to.be.false; - expect(disabled.stylesheet.perTrackMultiBarRest).to.equal(null); - expect(enabled.stylesheet.multiTrackMultiBarRest).to.be.true; - expect(enabled.stylesheet.perTrackMultiBarRest).to.be.ok; - expect(enabled.stylesheet.perTrackMultiBarRest!.has(0)).to.be.false; - expect(enabled.stylesheet.perTrackMultiBarRest!.has(1)).to.be.true; - expect(enabled.stylesheet.perTrackMultiBarRest!.has(2)).to.be.true; + expect(disabled.stylesheet.multiTrackMultiBarRest).toBe(false); + expect(disabled.stylesheet.perTrackMultiBarRest).toBe(null); + expect(enabled.stylesheet.multiTrackMultiBarRest).toBe(true); + expect(enabled.stylesheet.perTrackMultiBarRest).toBeTruthy(); + expect(enabled.stylesheet.perTrackMultiBarRest!.has(0)).toBe(false); + expect(enabled.stylesheet.perTrackMultiBarRest!.has(1)).toBe(true); + expect(enabled.stylesheet.perTrackMultiBarRest!.has(2)).toBe(true); }); it('header-footer', async () => { const score = (await prepareImporterWithFile('guitarpro8/header-footer.gp')).readScore(); - expect(score.style).to.be.ok; + expect(score.style).toBeTruthy(); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).to.equal('Title: %TITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Title)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.template).toBe('Title: %TITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Title)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).to.equal('Subtitle: %SUBTITLE%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.SubTitle)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.template).toBe('Subtitle: %SUBTITLE%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.SubTitle)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).to.equal('Artist: %ARTIST%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Artist)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.template).toBe('Artist: %ARTIST%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Artist)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).to.equal('Album: %ALBUM%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Album)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.template).toBe('Album: %ALBUM%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Album)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).to.equal('Words: %WORDS%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Words)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.template).toBe('Words: %WORDS%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Words)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).to.equal('Music: %MUSIC%'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.has(ScoreSubElement.Music)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.template).toBe('Music: %MUSIC%'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Music)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.WordsAndMusic)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.template).toBe( 'Words & Music: %MUSIC%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).to.equal(TextAlign.Left); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.WordsAndMusic)!.textAlign).toBe(TextAlign.Left); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.Transcriber)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.template).toBe( 'Transcriber: %TABBER%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.textAlign).to.equal(TextAlign.Center); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Transcriber)!.textAlign).toBe(TextAlign.Center); - expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.Copyright)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.template).toBe( 'Copyright: %COPYRIGHT%' ); - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).to.equal(TextAlign.Right); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.isVisible).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.Copyright)!.textAlign).toBe(TextAlign.Right); - expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).to.be.true; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).to.equal('Copyright2'); - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).to.be.false; - expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).to.equal( + expect(score.style!.headerAndFooter.has(ScoreSubElement.CopyrightSecondLine)).toBe(true); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.template).toBe('Copyright2'); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.isVisible).toBe(false); + expect(score.style!.headerAndFooter.get(ScoreSubElement.CopyrightSecondLine)!.textAlign).toBe( TextAlign.Right ); }); @@ -390,12 +390,12 @@ describe('Gp8ImporterTest', () => { const usedChannels = new Set(); for (const t of score.tracks) { - expect(Number.isNaN(t.playbackInfo.primaryChannel)).to.be.false; - expect(Number.isNaN(t.playbackInfo.secondaryChannel)).to.be.false; + expect(Number.isNaN(t.playbackInfo.primaryChannel)).toBe(false); + expect(Number.isNaN(t.playbackInfo.secondaryChannel)).toBe(false); if (t.playbackInfo.primaryChannel !== SynthConstants.PercussionChannel) { - expect(usedChannels.has(t.playbackInfo.primaryChannel)).to.be.false; - expect(usedChannels.has(t.playbackInfo.secondaryChannel)).to.be.false; + expect(usedChannels.has(t.playbackInfo.primaryChannel)).toBe(false); + expect(usedChannels.has(t.playbackInfo.secondaryChannel)).toBe(false); usedChannels.add(t.playbackInfo.primaryChannel); usedChannels.add(t.playbackInfo.secondaryChannel); @@ -409,39 +409,39 @@ describe('Gp8ImporterTest', () => { // track data not relevant for snapshots score.tracks = []; - expect(score).to.toMatchSnapshot(); + expect(score).toMatchSnapshot(); }); it('bank', async () => { const score = (await prepareImporterWithFile('guitarpro8/bank.gp')).readScore(); - expect(score.tracks[0].playbackInfo.program).to.equal(25); - expect(score.tracks[0].playbackInfo.bank).to.equal(0); + expect(score.tracks[0].playbackInfo.program).toBe(25); + expect(score.tracks[0].playbackInfo.bank).toBe(0); - expect(score.tracks[1].playbackInfo.program).to.equal(25); - expect(score.tracks[1].playbackInfo.bank).to.equal(77); + expect(score.tracks[1].playbackInfo.program).toBe(25); + expect(score.tracks[1].playbackInfo.bank).toBe(77); - expect(score.tracks[2].playbackInfo.program).to.equal(25); - expect(score.tracks[2].playbackInfo.bank).to.equal(256); + expect(score.tracks[2].playbackInfo.program).toBe(25); + expect(score.tracks[2].playbackInfo.bank).toBe(256); }); it('bank-change', async () => { const score = (await prepareImporterWithFile('guitarpro8/bank-change.gp')).readScore(); - expect(score.tracks[0].playbackInfo.program).to.equal(25); - expect(score.tracks[0].playbackInfo.bank).to.equal(0); + expect(score.tracks[0].playbackInfo.program).toBe(25); + expect(score.tracks[0].playbackInfo.bank).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].automations.length).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].automations.length).toBe(1); expect( score.tracks[0].staves[0].bars[0].voices[0].beats[0].getAutomation(AutomationType.Instrument)?.value - ).to.equal(25); - // expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getAutomation(AutomationType.Bank)?.value).to.equal(0); skipped + ).toBe(25); + // expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getAutomation(AutomationType.Bank)?.value).toBe(0); skipped - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].automations.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].automations.length).toBe(2); expect( score.tracks[0].staves[0].bars[1].voices[0].beats[0].getAutomation(AutomationType.Instrument)?.value - ).to.equal(25); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].getAutomation(AutomationType.Bank)?.value).to.equal( + ).toBe(25); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].getAutomation(AutomationType.Bank)?.value).toBe( 256 ); }); @@ -449,21 +449,21 @@ describe('Gp8ImporterTest', () => { it('extend-bar-lines', async () => { const score = (await prepareImporterWithFile('guitarpro8/extended-barlines.gp')).readScore(); - expect(score.stylesheet.extendBarLines).to.be.true; + expect(score.stylesheet.extendBarLines).toBe(true); }); describe('barnumbers', () => { it('all', async () => { const score = (await prepareImporterWithFile('guitarpro8/barnumbers-all.gp')).readScore(); - expect(score.stylesheet.barNumberDisplay).to.equal(BarNumberDisplay.AllBars); + expect(score.stylesheet.barNumberDisplay).toBe(BarNumberDisplay.AllBars); }); it('hide', async () => { const score = (await prepareImporterWithFile('guitarpro8/barnumbers-hide.gp')).readScore(); - expect(score.stylesheet.barNumberDisplay).to.equal(BarNumberDisplay.Hide); + expect(score.stylesheet.barNumberDisplay).toBe(BarNumberDisplay.Hide); }); it('first', async () => { const score = (await prepareImporterWithFile('guitarpro8/barnumbers-first.gp')).readScore(); - expect(score.stylesheet.barNumberDisplay).to.equal(BarNumberDisplay.FirstOfSystem); + expect(score.stylesheet.barNumberDisplay).toBe(BarNumberDisplay.FirstOfSystem); }); }); @@ -472,37 +472,33 @@ describe('Gp8ImporterTest', () => { // NOTE: no need to verify all details, we'll have a visual test for that. - expect(score.masterBars[0].beamingRules).to.be.ok; - expect(score.masterBars[0].beamingRules!.groups.has(Duration.Eighth)).to.be.true; - expect(score.masterBars[0].beamingRules!.groups.get(Duration.Eighth)!.join(',')).to.be.equal('2,2,2,2'); + expect(score.masterBars[0].beamingRules).toBeTruthy(); + expect(score.masterBars[0].beamingRules!.groups.has(Duration.Eighth)).toBe(true); + expect(score.masterBars[0].beamingRules!.groups.get(Duration.Eighth)!.join(',')).toBe('2,2,2,2'); // equal to previous - expect(score.masterBars[1].beamingRules === undefined, 'expected beamingRules of bar 1 to be undefined').to.be - .true; + expect(score.masterBars[1].beamingRules === undefined, 'expected beamingRules of bar 1 to be undefined').toBe(true); expect( score.masterBars[1].actualBeamingRules === score.masterBars[0].beamingRules, 'actualBeamingRules of bar 1 incorrect' - ).to.be.true; - expect(score.masterBars[2].beamingRules === undefined, 'expected beamingRules of bar 2 to be undefined').to.be - .true; + ).toBe(true); + expect(score.masterBars[2].beamingRules === undefined, 'expected beamingRules of bar 2 to be undefined').toBe(true); expect( score.masterBars[2].actualBeamingRules === score.masterBars[0].beamingRules, 'actualBeamingRules of bar 1 incorrect' - ).to.be.true; - expect(score.masterBars[3].beamingRules === undefined, 'expected beamingRules of bar 3 to be undefined').to.be - .true; + ).toBe(true); + expect(score.masterBars[3].beamingRules === undefined, 'expected beamingRules of bar 3 to be undefined').toBe(true); expect( score.masterBars[3].actualBeamingRules === score.masterBars[0].beamingRules, 'actualBeamingRules of bar 1 incorrect' - ).to.be.true; - expect(score.masterBars[4].beamingRules === undefined, 'expected beamingRules of bar 4 to be undefined').to.be - .true; + ).toBe(true); + expect(score.masterBars[4].beamingRules === undefined, 'expected beamingRules of bar 4 to be undefined').toBe(true); expect( score.masterBars[4].actualBeamingRules === score.masterBars[0].beamingRules, 'actualBeamingRules of bar 1 incorrect' - ).to.be.true; + ).toBe(true); - expect(score.masterBars[5].beamingRules!.groups.has(Duration.Eighth)).to.be.true; - expect(score.masterBars[5].beamingRules!.groups.get(Duration.Eighth)!.join(',')).to.be.equal('4,4'); + expect(score.masterBars[5].beamingRules!.groups.has(Duration.Eighth)).toBe(true); + expect(score.masterBars[5].beamingRules!.groups.get(Duration.Eighth)!.join(',')).toBe('4,4'); }); it('harmonics-lowercase', async () => { @@ -510,4 +506,14 @@ describe('Gp8ImporterTest', () => { const score = reader.readScore(); GpImporterTestHelper.checkHarmonics(score); }); + + it('orphan-tempo-automation', async () => { + // GPIF tempo automations can reference bar indices that are not + // present in the score's masterBars list (e.g. off-by-one or after + // bar deletion). Should be skipped instead of null-dereferencing. + const reader = await prepareImporterWithFile('guitarpro8/orphan-tempo-automation.gp'); + const score = reader.readScore(); + expect(score.masterBars.length).toBe(100); + expect(score.tracks.length).toBe(3); + }); }); diff --git a/packages/alphatab/test/importer/GpImporterTestHelper.ts b/packages/alphatab/test/importer/GpImporterTestHelper.ts index 269796297..0a5ab94ad 100644 --- a/packages/alphatab/test/importer/GpImporterTestHelper.ts +++ b/packages/alphatab/test/importer/GpImporterTestHelper.ts @@ -1,3 +1,4 @@ +import { expect } from 'vitest'; import { Gp3To5Importer } from '@coderline/alphatab/importer/Gp3To5Importer'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { AccentuationType } from '@coderline/alphatab/model/AccentuationType'; @@ -19,8 +20,6 @@ import type { Track } from '@coderline/alphatab/model/Track'; import { VibratoType } from '@coderline/alphatab/model/VibratoType'; import { Settings } from '@coderline/alphatab/Settings'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - /** * @internal */ @@ -53,418 +52,417 @@ export class GpImporterTestHelper { Duration.SixtyFourth ]; for (const duration of durationsInFile) { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).to.equal(duration); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toBe(duration); beat++; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).to.equal(duration); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toBe(duration); beat++; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).to.equal(duration); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toBe(duration); beat++; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).to.equal(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).to.equal(duration); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toBe(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toBe(duration); beat++; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].isRest).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).to.equal(duration); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].isRest).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toBe(duration); beat++; } } public static checkTimeSignatures(score: Score): void { - expect(score.masterBars[0].timeSignatureNumerator).to.equal(4); - expect(score.masterBars[0].timeSignatureDenominator).to.equal(4); + expect(score.masterBars[0].timeSignatureNumerator).toBe(4); + expect(score.masterBars[0].timeSignatureDenominator).toBe(4); - expect(score.masterBars[1].timeSignatureNumerator).to.equal(3); - expect(score.masterBars[1].timeSignatureDenominator).to.equal(4); + expect(score.masterBars[1].timeSignatureNumerator).toBe(3); + expect(score.masterBars[1].timeSignatureDenominator).toBe(4); - expect(score.masterBars[2].timeSignatureNumerator).to.equal(2); - expect(score.masterBars[2].timeSignatureDenominator).to.equal(4); + expect(score.masterBars[2].timeSignatureNumerator).toBe(2); + expect(score.masterBars[2].timeSignatureDenominator).toBe(4); - expect(score.masterBars[3].timeSignatureNumerator).to.equal(1); - expect(score.masterBars[3].timeSignatureDenominator).to.equal(4); + expect(score.masterBars[3].timeSignatureNumerator).toBe(1); + expect(score.masterBars[3].timeSignatureDenominator).toBe(4); - expect(score.masterBars[4].timeSignatureNumerator).to.equal(20); - expect(score.masterBars[4].timeSignatureDenominator).to.equal(32); + expect(score.masterBars[4].timeSignatureNumerator).toBe(20); + expect(score.masterBars[4].timeSignatureDenominator).toBe(32); } public static checkDead(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toBe(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isDead).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toBe(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isDead).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).toBe(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isDead).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].string).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].string).toBe(4); } public static checkGrace(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).to.equal(GraceType.BeforeBeat); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).to.equal(Duration.Quarter); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].graceType).to.equal(GraceType.BeforeBeat); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).to.equal(Duration.Eighth); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].duration).to.equal(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).toBe(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).toBe(Duration.Quarter); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].graceType).toBe(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).toBe(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].duration).toBe(Duration.Quarter); } public static checkAccentuations(score: Score, includeHeavy: boolean): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].accentuated).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].accentuated).toBe( AccentuationType.Normal ); if (includeHeavy) { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].accentuated).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].accentuated).toBe( AccentuationType.Heavy ); } - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).toBe(true); } public static checkHarmonics(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toBe( HarmonicType.Natural ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toBe( HarmonicType.Artificial ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).to.equal(HarmonicType.Tap); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).to.equal(HarmonicType.Semi); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).to.equal(HarmonicType.Pinch); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toBe(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toBe(HarmonicType.Semi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toBe(HarmonicType.Pinch); // TODO: Harmonic Values } public static checkHammer(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isHammerPullOrigin).to.equal(false); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].isHammerPullOrigin).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].isHammerPullOrigin).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].isHammerPullOrigin).to.be.equal(true); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].hammerPullOrigin).to.not.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[1].hammerPullOrigin).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[2].hammerPullOrigin).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[3].hammerPullOrigin).to.be.ok; - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isHammerPullOrigin).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isHammerPullOrigin).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].hammerPullOrigin).to.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isHammerPullOrigin).toBe(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].isHammerPullOrigin).toBe(true); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].hammerPullOrigin).not.toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[1].hammerPullOrigin).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[2].hammerPullOrigin).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[3].hammerPullOrigin).toBeTruthy(); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].hammerPullOrigin).toBeTruthy(); } public static checkBend(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toBe(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toBe(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).to.equal(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toBe(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).to.equal(10); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toBe(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toBe(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].offset).to.equal(20); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].value).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].offset).toBe(20); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].value).toBe(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].offset).to.equal(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].offset).toBe(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].offset).to.equal(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].offset).toBe(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].offset).to.equal(50); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].value).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].offset).toBe(50); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].value).toBe(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].value).to.equal(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].value).toBe(4); } public static checkTremolo(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toBe(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).to.equal(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toBe(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toBe(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toBe(3); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).to.equal(45); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toBe(45); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].value).toBe(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toBe(3); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).to.equal(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toBe(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).to.equal(45); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toBe(45); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).to.equal(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).to.equal(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toBe(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toBe(-4); } public static checkSlides(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).toBe( SlideOutType.Legato ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).toBe( SlideOutType.Shift ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].getNoteOnString(5)!.slideInType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].getNoteOnString(5)!.slideInType).toBe( SlideInType.IntoFromBelow ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].getNoteOnString(5)!.slideInType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].getNoteOnString(5)!.slideInType).toBe( SlideInType.IntoFromAbove ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].getNoteOnString(5)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].getNoteOnString(5)!.slideOutType).toBe( SlideOutType.OutDown ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].getNoteOnString(5)!.slideOutType).to.equal( + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].getNoteOnString(5)!.slideOutType).toBe( SlideOutType.OutUp ); } public static checkStrings(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(6); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(1)!.fret).to.equal(6); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(2)!.fret).to.equal(5); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(3)!.fret).to.equal(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(4)!.fret).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.fret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(6)!.fret).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toBe(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(1)!.fret).toBe(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(2)!.fret).toBe(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(3)!.fret).toBe(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(4)!.fret).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.fret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(6)!.fret).toBe(1); } public static checkVibrato(score: Score, checkNotes: boolean): void { if (checkNotes) { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato).to.equal(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato).toBe(VibratoType.Slight); } - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].vibrato).to.equal(VibratoType.Slight); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].vibrato).to.equal(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].vibrato).toBe(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].vibrato).toBe(VibratoType.Slight); } public static checkTrills(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).to.equal(Duration.Sixteenth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toBe(Duration.Sixteenth); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].isTremolo).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tremoloPicking!.marks).to.equal(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tremoloPicking!.marks).toBe(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].isTremolo).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tremoloPicking!.marks).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tremoloPicking!.marks).toBe(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].isTremolo).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].tremoloPicking!.marks).to.equal(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].tremoloPicking!.marks).toBe(1); } public static checkOtherEffects(score: Score, skipInstrumentCheck: boolean = false): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isStaccato).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isStaccato).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).toBe(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).toBe(true); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).to.equal('C'); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).to.equal('Text'); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).toBe('C'); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).toBe('Text'); - expect(score.masterBars[4].isDoubleBar).to.be.equal(true); - expect(score.masterBars[4].tempoAutomations.length).to.equal(1); - expect(score.masterBars[4].tempoAutomations[0]!.value).to.equal(120.0); + expect(score.masterBars[4].isDoubleBar).toBe(true); + expect(score.masterBars[4].tempoAutomations.length).toBe(1); + expect(score.masterBars[4].tempoAutomations[0]!.value).toBe(120.0); if (!skipInstrumentCheck) { - expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)).to.be - .ok; + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)).toBeTruthy(); expect( score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)!.value - ).to.equal(25); + ).toBe(25); } } public static checkFingering(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isFingering).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isFingering).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).toBe( Fingers.LittleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].rightHandFinger).to.equal(Fingers.Thumb); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].rightHandFinger).toBe(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].rightHandFinger).toBe( Fingers.IndexFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].rightHandFinger).toBe( Fingers.MiddleFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].rightHandFinger).toBe( Fingers.AnnularFinger ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].rightHandFinger).to.equal( + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].rightHandFinger).toBe( Fingers.LittleFinger ); } public static checkStroke(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).to.equal(BrushType.BrushDown); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).to.equal(BrushType.BrushUp); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].pickStroke).to.equal(PickStroke.Up); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].pickStroke).to.equal(PickStroke.Down); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).toBe(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).toBe(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].pickStroke).toBe(PickStroke.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].pickStroke).toBe(PickStroke.Down); } public static checkTuplets(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tupletNumerator).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tupletNumerator).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tupletNumerator).to.equal(3); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].tupletNumerator).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].tupletNumerator).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].tupletNumerator).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].tupletNumerator).to.equal(5); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].tupletNumerator).to.equal(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tupletNumerator).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tupletNumerator).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tupletNumerator).toBe(3); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].tupletNumerator).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].tupletNumerator).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].tupletNumerator).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].tupletNumerator).toBe(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].tupletNumerator).toBe(5); } public static checkRanges(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).to.be.equal(true); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).to.be.equal(true); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).to.be.equal(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).toBe(true); } public static checkEffects(_score: Score): void { // just check if reading works - expect(true).to.be.equal(true); + expect(true).toBe(true); } public static checkKeySignatures(score: Score): void { const bars = score.tracks[0].staves[0].bars; // major - flats - expect(bars[0].keySignature).to.equal(KeySignature.C); - expect(bars[0].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[1].keySignature).to.equal(KeySignature.F); - expect(bars[1].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[2].keySignature).to.equal(KeySignature.Bb); - expect(bars[2].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[3].keySignature).to.equal(KeySignature.Eb); - expect(bars[3].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[4].keySignature).to.equal(KeySignature.Ab); - expect(bars[4].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[5].keySignature).to.equal(KeySignature.Db); - expect(bars[5].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[6].keySignature).to.equal(KeySignature.Gb); - expect(bars[6].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[7].keySignature).to.equal(KeySignature.Cb); - expect(bars[7].keySignatureType).to.equal(KeySignatureType.Major); + expect(bars[0].keySignature).toBe(KeySignature.C); + expect(bars[0].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[1].keySignature).toBe(KeySignature.F); + expect(bars[1].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[2].keySignature).toBe(KeySignature.Bb); + expect(bars[2].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[3].keySignature).toBe(KeySignature.Eb); + expect(bars[3].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[4].keySignature).toBe(KeySignature.Ab); + expect(bars[4].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[5].keySignature).toBe(KeySignature.Db); + expect(bars[5].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[6].keySignature).toBe(KeySignature.Gb); + expect(bars[6].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[7].keySignature).toBe(KeySignature.Cb); + expect(bars[7].keySignatureType).toBe(KeySignatureType.Major); // major - sharps - expect(bars[8].keySignature).to.equal(KeySignature.C); - expect(bars[8].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[9].keySignature).to.equal(KeySignature.G); - expect(bars[9].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[10].keySignature).to.equal(KeySignature.D); - expect(bars[10].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[11].keySignature).to.equal(KeySignature.A); - expect(bars[11].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[12].keySignature).to.equal(KeySignature.E); - expect(bars[12].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[13].keySignature).to.equal(KeySignature.B); - expect(bars[13].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[14].keySignature).to.equal(KeySignature.FSharp); - expect(bars[14].keySignatureType).to.equal(KeySignatureType.Major); - expect(bars[15].keySignature).to.equal(KeySignature.CSharp); - expect(bars[15].keySignatureType).to.equal(KeySignatureType.Major); + expect(bars[8].keySignature).toBe(KeySignature.C); + expect(bars[8].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[9].keySignature).toBe(KeySignature.G); + expect(bars[9].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[10].keySignature).toBe(KeySignature.D); + expect(bars[10].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[11].keySignature).toBe(KeySignature.A); + expect(bars[11].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[12].keySignature).toBe(KeySignature.E); + expect(bars[12].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[13].keySignature).toBe(KeySignature.B); + expect(bars[13].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[14].keySignature).toBe(KeySignature.FSharp); + expect(bars[14].keySignatureType).toBe(KeySignatureType.Major); + expect(bars[15].keySignature).toBe(KeySignature.CSharp); + expect(bars[15].keySignatureType).toBe(KeySignatureType.Major); // minor flats - expect(bars[16].keySignature).to.equal(KeySignature.C); - expect(bars[16].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[17].keySignature).to.equal(KeySignature.F); - expect(bars[17].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[18].keySignature).to.equal(KeySignature.Bb); - expect(bars[18].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[19].keySignature).to.equal(KeySignature.Eb); - expect(bars[19].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[20].keySignature).to.equal(KeySignature.Ab); - expect(bars[20].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[21].keySignature).to.equal(KeySignature.Db); - expect(bars[21].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[22].keySignature).to.equal(KeySignature.Gb); - expect(bars[22].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[23].keySignature).to.equal(KeySignature.Cb); - expect(bars[23].keySignatureType).to.equal(KeySignatureType.Minor); + expect(bars[16].keySignature).toBe(KeySignature.C); + expect(bars[16].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[17].keySignature).toBe(KeySignature.F); + expect(bars[17].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[18].keySignature).toBe(KeySignature.Bb); + expect(bars[18].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[19].keySignature).toBe(KeySignature.Eb); + expect(bars[19].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[20].keySignature).toBe(KeySignature.Ab); + expect(bars[20].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[21].keySignature).toBe(KeySignature.Db); + expect(bars[21].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[22].keySignature).toBe(KeySignature.Gb); + expect(bars[22].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[23].keySignature).toBe(KeySignature.Cb); + expect(bars[23].keySignatureType).toBe(KeySignatureType.Minor); // minor sharps - expect(bars[24].keySignature).to.equal(KeySignature.C); - expect(bars[24].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[25].keySignature).to.equal(KeySignature.G); - expect(bars[25].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[26].keySignature).to.equal(KeySignature.D); - expect(bars[26].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[27].keySignature).to.equal(KeySignature.A); - expect(bars[27].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[28].keySignature).to.equal(KeySignature.E); - expect(bars[28].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[29].keySignature).to.equal(KeySignature.B); - expect(bars[29].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[30].keySignature).to.equal(KeySignature.FSharp); - expect(bars[30].keySignatureType).to.equal(KeySignatureType.Minor); - expect(bars[31].keySignature).to.equal(KeySignature.CSharp); - expect(bars[31].keySignatureType).to.equal(KeySignatureType.Minor); + expect(bars[24].keySignature).toBe(KeySignature.C); + expect(bars[24].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[25].keySignature).toBe(KeySignature.G); + expect(bars[25].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[26].keySignature).toBe(KeySignature.D); + expect(bars[26].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[27].keySignature).toBe(KeySignature.A); + expect(bars[27].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[28].keySignature).toBe(KeySignature.E); + expect(bars[28].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[29].keySignature).toBe(KeySignature.B); + expect(bars[29].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[30].keySignature).toBe(KeySignature.FSharp); + expect(bars[30].keySignatureType).toBe(KeySignatureType.Minor); + expect(bars[31].keySignature).toBe(KeySignature.CSharp); + expect(bars[31].keySignatureType).toBe(KeySignatureType.Minor); } public static checkColors(score: Score): void { - expect(score.tracks[0].name).to.equal('Red'); - expect(score.tracks[0].color.rgba).to.equal('#FF0000'); - expect(score.tracks[1].name).to.equal('Green'); - expect(score.tracks[1].color.rgba).to.equal('#00FF00'); - expect(score.tracks[2].name).to.equal('Yellow'); - expect(score.tracks[2].color.rgba).to.equal('#FFFF00'); - expect(score.tracks[3].name).to.equal('Blue'); - expect(score.tracks[3].color.rgba).to.equal('#0000FF'); + expect(score.tracks[0].name).toBe('Red'); + expect(score.tracks[0].color.rgba).toBe('#FF0000'); + expect(score.tracks[1].name).toBe('Green'); + expect(score.tracks[1].color.rgba).toBe('#00FF00'); + expect(score.tracks[2].name).toBe('Yellow'); + expect(score.tracks[2].color.rgba).toBe('#FFFF00'); + expect(score.tracks[3].name).toBe('Blue'); + expect(score.tracks[3].color.rgba).toBe('#0000FF'); } private static _createChord(name: string, firstFret: number, strings: number[], barreFrets?: number[]) { @@ -481,7 +479,7 @@ export class GpImporterTestHelper { public static checkChords(score: Score): void { const track: Track = score.tracks[0]; const staff: Staff = track.staves[0]; - expect(staff.chords!.size).to.equal(8); + expect(staff.chords!.size).toBe(8); GpImporterTestHelper.checkChord( GpImporterTestHelper._createChord('C', 1, [0, 1, 0, 2, 3, -1]), @@ -519,13 +517,13 @@ export class GpImporterTestHelper { } public static checkChord(expected: Chord | null, actual: Chord | null): void { - expect(actual === null).to.equal(expected === null); + expect(actual === null).toBe(expected === null); if (expected && actual) { - expect(actual.name).to.equal(expected.name); - expect(actual.firstFret).to.equal(expected.firstFret); - expect(actual.strings.length).to.equal(expected.strings.length); - expect(actual.strings.join(',')).to.equal(expected.strings.join(',')); - expect(actual.barreFrets.join(',')).to.equal(expected.barreFrets.join(',')); + expect(actual.name).toBe(expected.name); + expect(actual.firstFret).toBe(expected.firstFret); + expect(actual.strings.length).toBe(expected.strings.length); + expect(actual.strings.join(',')).toBe(expected.strings.join(',')); + expect(actual.barreFrets.join(',')).toBe(expected.barreFrets.join(',')); } } @@ -535,33 +533,33 @@ export class GpImporterTestHelper { trackAll: Score, track1And3: Score ): void { - expect(track1.tracks[0].isVisibleOnMultiTrack).to.be.true; - expect(track1.tracks[1].isVisibleOnMultiTrack).to.be.false; - expect(track1.tracks[2].isVisibleOnMultiTrack).to.be.false; + expect(track1.tracks[0].isVisibleOnMultiTrack).toBe(true); + expect(track1.tracks[1].isVisibleOnMultiTrack).toBe(false); + expect(track1.tracks[2].isVisibleOnMultiTrack).toBe(false); - expect(track2.tracks[0].isVisibleOnMultiTrack).to.be.false; - expect(track2.tracks[1].isVisibleOnMultiTrack).to.be.true; - expect(track2.tracks[2].isVisibleOnMultiTrack).to.be.false; + expect(track2.tracks[0].isVisibleOnMultiTrack).toBe(false); + expect(track2.tracks[1].isVisibleOnMultiTrack).toBe(true); + expect(track2.tracks[2].isVisibleOnMultiTrack).toBe(false); - expect(trackAll.tracks[0].isVisibleOnMultiTrack).to.be.true; - expect(trackAll.tracks[1].isVisibleOnMultiTrack).to.be.true; - expect(trackAll.tracks[2].isVisibleOnMultiTrack).to.be.true; + expect(trackAll.tracks[0].isVisibleOnMultiTrack).toBe(true); + expect(trackAll.tracks[1].isVisibleOnMultiTrack).toBe(true); + expect(trackAll.tracks[2].isVisibleOnMultiTrack).toBe(true); - expect(track1And3.tracks[0].isVisibleOnMultiTrack).to.be.true; - expect(track1And3.tracks[1].isVisibleOnMultiTrack).to.be.false; - expect(track1And3.tracks[2].isVisibleOnMultiTrack).to.be.true; + expect(track1And3.tracks[0].isVisibleOnMultiTrack).toBe(true); + expect(track1And3.tracks[1].isVisibleOnMultiTrack).toBe(false); + expect(track1And3.tracks[2].isVisibleOnMultiTrack).toBe(true); } public static checkSlash(score: Score): void { - expect(score.tracks.length).to.equal(2); + expect(score.tracks.length).toBe(2); - expect(score.tracks[0].staves.length).to.equal(1); - expect(score.tracks[0].staves[0].showSlash).to.equal(true); - expect(score.tracks[0].staves[0].showTablature).to.equal(true); - expect(score.tracks[0].staves[0].showStandardNotation).to.equal(true); + expect(score.tracks[0].staves.length).toBe(1); + expect(score.tracks[0].staves[0].showSlash).toBe(true); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); - expect(score.tracks[1].staves.length).to.equal(1); - expect(score.tracks[1].staves[0].showSlash).to.equal(false); - expect(score.tracks[1].staves[0].showTablature).to.equal(true); - expect(score.tracks[1].staves[0].showStandardNotation).to.equal(true); + expect(score.tracks[1].staves.length).toBe(1); + expect(score.tracks[1].staves[0].showSlash).toBe(false); + expect(score.tracks[1].staves[0].showTablature).toBe(true); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); } } diff --git a/packages/alphatab/test/importer/GpxImporter.test.ts b/packages/alphatab/test/importer/GpxImporter.test.ts index d4c4be0c9..a2c8bc070 100644 --- a/packages/alphatab/test/importer/GpxImporter.test.ts +++ b/packages/alphatab/test/importer/GpxImporter.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { type GpxFile, GpxFileSystem } from '@coderline/alphatab/importer/GpxFileSystem'; import { GpxImporter } from '@coderline/alphatab/importer/GpxImporter'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; @@ -6,8 +7,6 @@ import { Settings } from '@coderline/alphatab/Settings'; import { Logger } from '@coderline/alphatab/Logger'; import { GpImporterTestHelper } from 'test/importer/GpImporterTestHelper'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - describe('GpxImporterTest', () => { async function prepareImporterWithFile(name: string): Promise { const data = await TestPlatform.loadFile(`test-data/${name}`); @@ -35,28 +34,28 @@ describe('GpxImporterTest', () => { for (let i: number = 0; i < fileSystem.files.length; i++) { const file: GpxFile = fileSystem.files[i]; Logger.info('Test', `${file.fileName} - ${file.fileSize}`); - expect(file.fileName).to.equal(names[i]); - expect(file.fileSize).to.equal(sizes[i]); + expect(file.fileName).toBe(names[i]); + expect(file.fileSize).toBe(sizes[i]); } }); it('score-info', async () => { const reader = await prepareImporterWithFile('guitarpro6/score-info.gpx'); const score: Score = reader.readScore(); - expect(score.title).to.equal('Title'); - expect(score.subTitle).to.equal('Subtitle'); - expect(score.artist).to.equal('Artist'); - expect(score.album).to.equal('Album'); - expect(score.words).to.equal('Words'); - expect(score.music).to.equal('Music'); - expect(score.copyright).to.equal('Copyright'); - expect(score.tab).to.equal('Tab'); - expect(score.instructions).to.equal('Instructions'); - expect(score.notices).to.equal('Notice1\nNotice2'); - expect(score.masterBars.length).to.equal(5); - expect(score.tracks.length).to.equal(2); - expect(score.tracks[0].name).to.equal('Track 1'); - expect(score.tracks[1].name).to.equal('Track 2'); + expect(score.title).toBe('Title'); + expect(score.subTitle).toBe('Subtitle'); + expect(score.artist).toBe('Artist'); + expect(score.album).toBe('Album'); + expect(score.words).toBe('Words'); + expect(score.music).toBe('Music'); + expect(score.copyright).toBe('Copyright'); + expect(score.tab).toBe('Tab'); + expect(score.instructions).toBe('Instructions'); + expect(score.notices).toBe('Notice1\nNotice2'); + expect(score.masterBars.length).toBe(5); + expect(score.tracks.length).toBe(2); + expect(score.tracks[0].name).toBe('Track 1'); + expect(score.tracks[1].name).toBe('Track 2'); }); it('notes', async () => { @@ -104,138 +103,87 @@ describe('GpxImporterTest', () => { it('bends', async () => { const reader = await prepareImporterWithFile('guitarpro6/bends.gpx'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).to.be.closeTo( - 0, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).to.equal(4); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).to.equal(2); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).to.be.closeTo( - 0, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).to.equal(4); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(4); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).to.be.closeTo( - 0, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).to.be.closeTo( - 0, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).to.equal(12); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).to.equal(12); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].value).to.equal(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toBe(2); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toBe(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toBe(2); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toBe(4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).toBe(4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).toBe(12); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).toBe(12); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].value).toBe(6); }); it('tremolo', async () => { const reader = await prepareImporterWithFile('guitarpro6/tremolo.gpx'); const score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); - - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(-4); - - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).to.equal(3); - - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); - - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 30, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-4); - - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 60, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).to.equal(-4); - - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).to.equal(4); - - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).to.be.closeTo(0, 0.001); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).to.equal(-4); - - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).to.be.closeTo( - 15, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12); - - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).to.be.closeTo( - 30.6, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).to.equal(-12); - - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).to.be.closeTo( - 45, - 0.001 - ); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).to.equal(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toBe(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toBe(2); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toBe(-4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toBe(3); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toBe(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toBe(-4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60, 3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toBe(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).toBe(4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).toBe(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(15, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).toBe(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30.6, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).toBe(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(45, 3); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).toBe(0); }); it('slides', async () => { diff --git a/packages/alphatab/test/importer/MusicXmlImporter.test.ts b/packages/alphatab/test/importer/MusicXmlImporter.test.ts index d5fb108f7..2ad1577c6 100644 --- a/packages/alphatab/test/importer/MusicXmlImporter.test.ts +++ b/packages/alphatab/test/importer/MusicXmlImporter.test.ts @@ -1,8 +1,8 @@ +import { describe, expect, it } from 'vitest'; import { BendType } from '@coderline/alphatab/model/BendType'; import { JsonConverter } from '@coderline/alphatab/model/JsonConverter'; import { BarNumberDisplay } from '@coderline/alphatab/model/RenderStylesheet'; import type { Score } from '@coderline/alphatab/model/Score'; -import { expect } from 'chai'; import { MusicXmlImporterTestHelper } from 'test/importer/MusicXmlImporterTestHelper'; describe('MusicXmlImporterTests', () => { @@ -11,11 +11,11 @@ describe('MusicXmlImporterTests', () => { 'test-data/musicxml3/track-volume-balance.musicxml' ); - expect(score.tracks[0].playbackInfo.volume).to.be.equal(16); - expect(score.tracks[1].playbackInfo.volume).to.be.equal(12); - expect(score.tracks[2].playbackInfo.volume).to.be.equal(8); - expect(score.tracks[3].playbackInfo.volume).to.be.equal(4); - expect(score.tracks[4].playbackInfo.volume).to.be.equal(0); + expect(score.tracks[0].playbackInfo.volume).toBe(16); + expect(score.tracks[1].playbackInfo.volume).toBe(12); + expect(score.tracks[2].playbackInfo.volume).toBe(8); + expect(score.tracks[3].playbackInfo.volume).toBe(4); + expect(score.tracks[4].playbackInfo.volume).toBe(0); }); it('track-balance', async () => { @@ -23,11 +23,11 @@ describe('MusicXmlImporterTests', () => { 'test-data/musicxml3/track-volume-balance.musicxml' ); - expect(score.tracks[0].playbackInfo.balance).to.be.equal(0); - expect(score.tracks[1].playbackInfo.balance).to.be.equal(4); - expect(score.tracks[2].playbackInfo.balance).to.be.equal(8); - expect(score.tracks[3].playbackInfo.balance).to.be.equal(12); - expect(score.tracks[4].playbackInfo.balance).to.be.equal(16); + expect(score.tracks[0].playbackInfo.balance).toBe(0); + expect(score.tracks[1].playbackInfo.balance).toBe(4); + expect(score.tracks[2].playbackInfo.balance).toBe(8); + expect(score.tracks[3].playbackInfo.balance).toBe(12); + expect(score.tracks[4].playbackInfo.balance).toBe(16); }); it('full-bar-rest', async () => { @@ -35,9 +35,9 @@ describe('MusicXmlImporterTests', () => { 'test-data/musicxml3/full-bar-rest.musicxml' ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isFullBarRest).to.be.true; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].isFullBarRest).to.be.true; - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].isFullBarRest).to.be.true; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isFullBarRest).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].isFullBarRest).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].isFullBarRest).toBe(true); }); it('first-bar-tempo', async () => { @@ -45,179 +45,179 @@ describe('MusicXmlImporterTests', () => { 'test-data/musicxml3/first-bar-tempo.musicxml' ); - expect(score.tempo).to.be.equal(60); - expect(score.masterBars[0].tempoAutomations.length).to.equal(1); - expect(score.masterBars[0].tempoAutomations[0]?.value).to.be.equal(60); - expect(score.masterBars[1].tempoAutomations.length).to.equal(1); - expect(score.masterBars[1].tempoAutomations[0].value).to.be.equal(60); + expect(score.tempo).toBe(60); + expect(score.masterBars[0].tempoAutomations.length).toBe(1); + expect(score.masterBars[0].tempoAutomations[0]?.value).toBe(60); + expect(score.masterBars[1].tempoAutomations.length).toBe(1); + expect(score.masterBars[1].tempoAutomations[0].value).toBe(60); }); it('tie-destination', async () => { let score: Score = await MusicXmlImporterTestHelper.testReferenceFile( 'test-data/musicxml3/tie-destination.musicxml' ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieOrigin).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].tieDestination).to.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].tieDestination).toBeTruthy(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isTieDestination).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].tieOrigin).to.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isTieDestination).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].tieOrigin).toBeTruthy(); score = JsonConverter.jsObjectToScore(JsonConverter.scoreToJsObject(score)); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieOrigin).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].tieDestination).to.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isTieOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].tieDestination).toBeTruthy(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isTieDestination).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].tieOrigin).to.be.ok; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isTieDestination).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].tieOrigin).toBeTruthy(); }); it('chord-diagram', async () => { let score: Score = await MusicXmlImporterTestHelper.testReferenceFile( 'test-data/musicxml3/chord-diagram.musicxml' ); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).to.equal('C'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[0]).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[1]).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[2]).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[3]).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[4]).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[5]).to.equal(-1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).toBe('C'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[0]).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[1]).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[2]).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[3]).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[4]).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[5]).toBe(-1); score = JsonConverter.jsObjectToScore(JsonConverter.scoreToJsObject(score)); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).to.be.ok; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).to.equal('C'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[0]).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[1]).to.equal(1); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[2]).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[3]).to.equal(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[4]).to.equal(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[5]).to.equal(-1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.name).toBe('C'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[0]).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[1]).toBe(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[2]).toBe(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[3]).toBe(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[4]).toBe(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].chord!.strings[5]).toBe(-1); }); it('compressed', async () => { const score: Score = await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml3/compressed.mxl'); - expect(score.title).to.equal('Title'); - expect(score.tracks.length).to.equal(1); - expect(score.masterBars.length).to.equal(1); + expect(score.title).toBe('Title'); + expect(score.tracks.length).toBe(1); + expect(score.masterBars.length).toBe(1); }); it('bend', async () => { const score: Score = await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml4/bends.xml'); let note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.equal(60); - expect(note.bendPoints![1].value).to.equal(2); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBe(60); + expect(note.bendPoints![1].value).toBe(2); note = score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.Prebend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(4); - expect(note.bendPoints![1].offset).to.equal(60); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.Prebend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(4); + expect(note.bendPoints![1].offset).toBe(60); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.equal(30); - expect(note.bendPoints![1].value).to.equal(4); - expect(note.bendPoints![2].offset).to.equal(30); - expect(note.bendPoints![2].value).to.equal(4); - expect(note.bendPoints![3].offset).to.equal(60); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBe(30); + expect(note.bendPoints![1].value).toBe(4); + expect(note.bendPoints![2].offset).toBe(30); + expect(note.bendPoints![2].value).toBe(4); + expect(note.bendPoints![3].offset).toBe(60); + expect(note.bendPoints![3].value).toBe(0); note = score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendRelease); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(2); - expect(note.bendPoints![1].offset).to.equal(60); - expect(note.bendPoints![1].value).to.equal(0); + expect(note.bendType).toBe(BendType.PrebendRelease); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(2); + expect(note.bendPoints![1].offset).toBe(60); + expect(note.bendPoints![1].value).toBe(0); note = score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendBend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(2); - expect(note.bendPoints![1].offset).to.equal(60); - expect(note.bendPoints![1].value).to.equal(4); + expect(note.bendType).toBe(BendType.PrebendBend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(2); + expect(note.bendPoints![1].offset).toBe(60); + expect(note.bendPoints![1].value).toBe(4); note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.equal(30); - expect(note.bendPoints![1].value).to.equal(2); - expect(note.bendPoints![2].offset).to.equal(30); - expect(note.bendPoints![2].value).to.equal(2); - expect(note.bendPoints![3].offset).to.equal(60); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBe(30); + expect(note.bendPoints![1].value).toBe(2); + expect(note.bendPoints![2].offset).toBe(30); + expect(note.bendPoints![2].value).toBe(2); + expect(note.bendPoints![3].offset).toBe(60); + expect(note.bendPoints![3].value).toBe(0); note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[1]; - expect(note.bendType).to.equal(BendType.BendRelease); - expect(note.bendPoints!.length).to.equal(4); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.equal(30); - expect(note.bendPoints![1].value).to.equal(2); - expect(note.bendPoints![2].offset).to.equal(30); - expect(note.bendPoints![2].value).to.equal(2); - expect(note.bendPoints![3].offset).to.equal(60); - expect(note.bendPoints![3].value).to.equal(0); + expect(note.bendType).toBe(BendType.BendRelease); + expect(note.bendPoints!.length).toBe(4); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBe(30); + expect(note.bendPoints![1].value).toBe(2); + expect(note.bendPoints![2].offset).toBe(30); + expect(note.bendPoints![2].value).toBe(2); + expect(note.bendPoints![3].offset).toBe(60); + expect(note.bendPoints![3].value).toBe(0); note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[2]; - expect(note.bendType).to.equal(BendType.None); + expect(note.bendType).toBe(BendType.None); note = score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0]; - expect(note.bendType).to.equal(BendType.Custom); - expect(note.bendPoints!.length).to.equal(12); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(1); - expect(note.bendPoints![1].offset).to.equal(10); - expect(note.bendPoints![1].value).to.equal(1); - expect(note.bendPoints![2].offset).to.equal(10); - expect(note.bendPoints![2].value).to.equal(1); - expect(note.bendPoints![3].offset).to.equal(20); - expect(note.bendPoints![3].value).to.equal(3); - expect(note.bendPoints![4].offset).to.equal(20); - expect(note.bendPoints![4].value).to.equal(3); - expect(note.bendPoints![5].offset).to.equal(30); - expect(note.bendPoints![5].value).to.equal(4); - expect(note.bendPoints![6].offset).to.equal(30); - expect(note.bendPoints![6].value).to.equal(4); - expect(note.bendPoints![7].offset).to.equal(40); - expect(note.bendPoints![7].value).to.equal(8); - expect(note.bendPoints![8].offset).to.equal(40); - expect(note.bendPoints![8].value).to.equal(8); - expect(note.bendPoints![9].offset).to.equal(50); - expect(note.bendPoints![9].value).to.equal(4); - expect(note.bendPoints![10].offset).to.equal(50); - expect(note.bendPoints![10].value).to.equal(4); - expect(note.bendPoints![11].offset).to.equal(60); - expect(note.bendPoints![11].value).to.equal(8); + expect(note.bendType).toBe(BendType.Custom); + expect(note.bendPoints!.length).toBe(12); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(1); + expect(note.bendPoints![1].offset).toBe(10); + expect(note.bendPoints![1].value).toBe(1); + expect(note.bendPoints![2].offset).toBe(10); + expect(note.bendPoints![2].value).toBe(1); + expect(note.bendPoints![3].offset).toBe(20); + expect(note.bendPoints![3].value).toBe(3); + expect(note.bendPoints![4].offset).toBe(20); + expect(note.bendPoints![4].value).toBe(3); + expect(note.bendPoints![5].offset).toBe(30); + expect(note.bendPoints![5].value).toBe(4); + expect(note.bendPoints![6].offset).toBe(30); + expect(note.bendPoints![6].value).toBe(4); + expect(note.bendPoints![7].offset).toBe(40); + expect(note.bendPoints![7].value).toBe(8); + expect(note.bendPoints![8].offset).toBe(40); + expect(note.bendPoints![8].value).toBe(8); + expect(note.bendPoints![9].offset).toBe(50); + expect(note.bendPoints![9].value).toBe(4); + expect(note.bendPoints![10].offset).toBe(50); + expect(note.bendPoints![10].value).toBe(4); + expect(note.bendPoints![11].offset).toBe(60); + expect(note.bendPoints![11].value).toBe(8); note = score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0]; - expect(note.bendType).to.equal(BendType.PrebendRelease); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(8); - expect(note.bendPoints![1].offset).to.equal(60); - expect(note.bendPoints![1].value).to.equal(0); + expect(note.bendType).toBe(BendType.PrebendRelease); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(8); + expect(note.bendPoints![1].offset).toBe(60); + expect(note.bendPoints![1].value).toBe(0); note = score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0]; - expect(note.bendType).to.equal(BendType.Bend); - expect(note.bendPoints!.length).to.equal(2); - expect(note.bendPoints![0].offset).to.equal(0); - expect(note.bendPoints![0].value).to.equal(0); - expect(note.bendPoints![1].offset).to.equal(30); - expect(note.bendPoints![1].value).to.equal(2); + expect(note.bendType).toBe(BendType.Bend); + expect(note.bendPoints!.length).toBe(2); + expect(note.bendPoints![0].offset).toBe(0); + expect(note.bendPoints![0].value).toBe(0); + expect(note.bendPoints![1].offset).toBe(30); + expect(note.bendPoints![1].value).toBe(2); }); it('partwise-basic', async () => { @@ -263,11 +263,11 @@ describe('MusicXmlImporterTests', () => { it('bank', async () => { const score = await MusicXmlImporterTestHelper.loadFile('test-data/musicxml4/midi-bank.xml'); - expect(score.tracks[0].playbackInfo.program).to.equal(0); - expect(score.tracks[0].playbackInfo.bank).to.equal(0); + expect(score.tracks[0].playbackInfo.program).toBe(0); + expect(score.tracks[0].playbackInfo.bank).toBe(0); - expect(score.tracks[1].playbackInfo.program).to.equal(1); - expect(score.tracks[1].playbackInfo.bank).to.equal(77); + expect(score.tracks[1].playbackInfo.program).toBe(1); + expect(score.tracks[1].playbackInfo.bank).toBe(77); }); it('buzzroll', async () => { @@ -275,17 +275,31 @@ describe('MusicXmlImporterTests', () => { expect(score).toMatchSnapshot(); }); + it('percussion-articulation', async () => { + const score = await MusicXmlImporterTestHelper.loadFile('test-data/musicxml4/percussion-articulation.xml'); + const notes = score.tracks[0].staves[0].bars[0].voices[0].beats.flatMap(b => b.notes); + + expect(notes).toHaveLength(2); + expect(notes[0].displayValue).toBe(38); + expect(notes[0].isPercussion).toBe(true); + expect(notes[0].percussionArticulation).toBe(38); + + expect(notes[1].displayValue).toBe(49); + expect(notes[1].isPercussion).toBe(true); + expect(notes[1].percussionArticulation).toBe(49); + }); + describe('barnumberdisplay', async () => { async function testPartwise(filename: string, display: BarNumberDisplay) { const score = await MusicXmlImporterTestHelper.loadFile(`test-data/musicxml4/${filename}`); - expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).to.equal(display); - expect(score.tracks[1].staves[0].bars[2].barNumberDisplay).to.equal(display); + expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).toBe(display); + expect(score.tracks[1].staves[0].bars[2].barNumberDisplay).toBe(display); } async function testTimewise(filename: string, display: BarNumberDisplay) { const score = await MusicXmlImporterTestHelper.loadFile(`test-data/musicxml4/${filename}`); - expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).to.equal(display); - expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).to.equal(display); + expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).toBe(display); + expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).toBe(display); } it('partwise-none', async () => @@ -296,12 +310,12 @@ describe('MusicXmlImporterTests', () => { await testPartwise('partwise-measure-numbering-system.xml', BarNumberDisplay.FirstOfSystem)); it('partwise-implicit', async () => { const score = await MusicXmlImporterTestHelper.loadFile('test-data/musicxml4/partwise-anacrusis.xml'); - expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).to.be.undefined; - expect(score.tracks[0].staves[0].bars[3].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[1].staves[0].bars[0].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).to.be.undefined; - expect(score.tracks[1].staves[0].bars[3].barNumberDisplay).to.equal(BarNumberDisplay.Hide); + expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).toBeUndefined(); + expect(score.tracks[0].staves[0].bars[3].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[1].staves[0].bars[0].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).toBeUndefined(); + expect(score.tracks[1].staves[0].bars[3].barNumberDisplay).toBe(BarNumberDisplay.Hide); }); it('timewise-none', async () => @@ -312,12 +326,12 @@ describe('MusicXmlImporterTests', () => { await testTimewise('timewise-measure-numbering-system.xml', BarNumberDisplay.FirstOfSystem)); it('timewise-implicit', async () => { const score = await MusicXmlImporterTestHelper.loadFile('test-data/musicxml4/timewise-anacrusis.xml'); - expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).to.be.undefined; - expect(score.tracks[0].staves[0].bars[3].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[1].staves[0].bars[0].barNumberDisplay).to.equal(BarNumberDisplay.Hide); - expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).to.be.undefined; - expect(score.tracks[1].staves[0].bars[3].barNumberDisplay).to.equal(BarNumberDisplay.Hide); + expect(score.tracks[0].staves[0].bars[0].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[0].staves[0].bars[1].barNumberDisplay).toBeUndefined(); + expect(score.tracks[0].staves[0].bars[3].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[1].staves[0].bars[0].barNumberDisplay).toBe(BarNumberDisplay.Hide); + expect(score.tracks[1].staves[0].bars[1].barNumberDisplay).toBeUndefined(); + expect(score.tracks[1].staves[0].bars[3].barNumberDisplay).toBe(BarNumberDisplay.Hide); }); }); }); diff --git a/packages/alphatab/test/importer/MusicXmlImporterSamples.test.ts b/packages/alphatab/test/importer/MusicXmlImporterSamples.test.ts index ce9880bf3..9aa1ea2da 100644 --- a/packages/alphatab/test/importer/MusicXmlImporterSamples.test.ts +++ b/packages/alphatab/test/importer/MusicXmlImporterSamples.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { MusicXmlImporterTestHelper } from 'test/importer/MusicXmlImporterTestHelper'; describe('MusicXmlImporterSamplesTests', () => { diff --git a/packages/alphatab/test/importer/MusicXmlImporterTestHelper.ts b/packages/alphatab/test/importer/MusicXmlImporterTestHelper.ts index 055823d79..6a6f3610f 100644 --- a/packages/alphatab/test/importer/MusicXmlImporterTestHelper.ts +++ b/packages/alphatab/test/importer/MusicXmlImporterTestHelper.ts @@ -12,7 +12,6 @@ import { Staff } from '@coderline/alphatab/model/Staff'; import { Track } from '@coderline/alphatab/model/Track'; import { Voice } from '@coderline/alphatab/model/Voice'; import { Settings } from '@coderline/alphatab/Settings'; -import { assert } from 'chai'; import { ComparisonHelpers } from 'test/model/ComparisonHelpers'; import { TestPlatform } from 'test/TestPlatform'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from 'test/visualTests/VisualTestHelper'; @@ -48,7 +47,7 @@ export class MusicXmlImporterTestHelper { score = importer.readScore(); } catch (e) { if (e instanceof UnsupportedFormatError) { - assert.fail(`Failed to load file ${file}: ${e}`); + throw new Error(`Failed to load file ${file}: ${e}`); } throw e; } @@ -62,7 +61,7 @@ export class MusicXmlImporterTestHelper { ComparisonHelpers.expectJsonEqual(expectedJson, actualJson, `<${file}>`, null); } catch (e) { - assert.fail((e as Error).message + (e as Error).stack); + throw new Error((e as Error).message + (e as Error).stack); } if (render) { diff --git a/packages/alphatab/test/importer/MusicXmlImporterTestSuite.test.ts b/packages/alphatab/test/importer/MusicXmlImporterTestSuite.test.ts index 3fcc5460d..2c37a3eeb 100644 --- a/packages/alphatab/test/importer/MusicXmlImporterTestSuite.test.ts +++ b/packages/alphatab/test/importer/MusicXmlImporterTestSuite.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { MusicXmlImporterTestHelper } from 'test/importer/MusicXmlImporterTestHelper'; describe('MusicXmlImporterTestSuiteTests', () => { diff --git a/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap index b70ee1495..26a102a43 100644 --- a/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap @@ -1,12 +1,12 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -18,27 +18,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -60,7 +60,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -80,13 +80,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-staff-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-staff-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -98,27 +98,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -140,7 +140,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -152,15 +152,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -177,7 +177,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -197,13 +197,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-staff-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-staff-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -215,27 +215,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -257,7 +257,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -277,13 +277,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-staff-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-staff-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -295,27 +295,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -333,7 +333,7 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -350,7 +350,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -370,13 +370,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -388,27 +388,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -430,7 +430,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -452,13 +452,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -470,27 +470,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -512,7 +512,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -534,13 +534,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-staff-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-staff-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -552,27 +552,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -594,7 +594,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -606,15 +606,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -631,7 +631,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -653,13 +653,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-staff-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-staff-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -671,27 +671,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -713,7 +713,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -735,13 +735,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-staff-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-staff-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -753,27 +753,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -791,7 +791,7 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -808,7 +808,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -830,13 +830,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-track 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-track 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -848,27 +848,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -890,7 +890,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -910,23 +910,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -948,7 +948,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -971,13 +971,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-track-meta 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-track-meta 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -989,27 +989,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1031,7 +1031,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1051,24 +1051,24 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, "clef" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1090,7 +1090,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1113,13 +1113,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1131,27 +1131,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1173,7 +1173,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1195,13 +1195,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-track-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-track-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1213,27 +1213,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1251,7 +1251,7 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -1268,7 +1268,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1290,13 +1290,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1308,27 +1308,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1350,7 +1350,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1370,13 +1370,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving initial meta-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > initial > meta-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1388,27 +1388,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1426,7 +1426,7 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -1443,7 +1443,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1463,13 +1463,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1489,25 +1489,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -1515,7 +1515,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1535,15 +1535,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -1562,11 +1562,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -1583,15 +1583,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -1606,11 +1606,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -1625,11 +1625,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -1646,7 +1646,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1666,13 +1666,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-staff-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-staff-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1692,25 +1692,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -1718,7 +1718,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1738,15 +1738,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -1765,11 +1765,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -1786,15 +1786,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -1809,11 +1809,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -1828,11 +1828,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -1849,7 +1849,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1861,15 +1861,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -1884,11 +1884,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -1903,11 +1903,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -1924,7 +1924,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -1944,13 +1944,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-staff-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-staff-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1970,25 +1970,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -1996,7 +1996,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2016,15 +2016,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2043,11 +2043,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -2064,15 +2064,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -2087,11 +2087,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -2106,11 +2106,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -2127,7 +2127,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -2147,13 +2147,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-staff-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-staff-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -2173,25 +2173,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -2199,7 +2199,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2219,15 +2219,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2246,11 +2246,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -2267,15 +2267,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -2288,7 +2288,7 @@ Map { Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -2303,11 +2303,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -2320,7 +2320,7 @@ Map { Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -2335,11 +2335,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -2352,7 +2352,7 @@ Map { Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -2369,7 +2369,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -2389,13 +2389,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -2415,25 +2415,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -2441,7 +2441,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2461,15 +2461,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2488,11 +2488,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -2515,23 +2515,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2551,11 +2551,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -2570,11 +2570,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -2591,7 +2591,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -2614,13 +2614,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -2640,25 +2640,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -2666,7 +2666,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2686,15 +2686,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2713,11 +2713,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -2740,23 +2740,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2776,11 +2776,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -2795,11 +2795,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -2816,7 +2816,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -2839,13 +2839,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-staff-staff 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-staff-staff 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -2865,25 +2865,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -2891,7 +2891,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2911,15 +2911,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2938,11 +2938,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -2965,23 +2965,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3001,11 +3001,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -3020,11 +3020,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -3041,7 +3041,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3053,15 +3053,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -3076,11 +3076,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -3095,11 +3095,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -3116,7 +3116,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3139,13 +3139,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-staff-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-staff-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3165,25 +3165,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -3191,7 +3191,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3211,15 +3211,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -3238,11 +3238,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -3265,23 +3265,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3301,11 +3301,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -3320,11 +3320,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -3341,7 +3341,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3364,13 +3364,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-staff-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-staff-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3390,25 +3390,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -3416,7 +3416,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3436,15 +3436,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -3463,11 +3463,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -3490,23 +3490,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3524,7 +3524,7 @@ Map { Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -3539,11 +3539,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -3556,7 +3556,7 @@ Map { Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -3571,11 +3571,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -3588,7 +3588,7 @@ Map { Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -3605,7 +3605,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3628,13 +3628,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-track 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-track 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3654,25 +3654,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -3680,7 +3680,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3700,15 +3700,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -3727,11 +3727,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -3754,23 +3754,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3790,11 +3790,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -3809,11 +3809,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -3830,7 +3830,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3851,23 +3851,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3887,11 +3887,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -3906,11 +3906,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -3927,7 +3927,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -3950,13 +3950,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-track-meta 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-track-meta 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3976,25 +3976,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -4002,7 +4002,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4022,15 +4022,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -4049,11 +4049,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -4076,23 +4076,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4112,11 +4112,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -4131,11 +4131,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -4152,7 +4152,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -4173,24 +4173,24 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, "clef" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4211,11 +4211,11 @@ Map { "__kind" => "Bar", "id" => 7, "clef" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -4231,11 +4231,11 @@ Map { "__kind" => "Bar", "id" => 8, "clef" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -4252,7 +4252,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -4275,13 +4275,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -4301,25 +4301,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -4327,7 +4327,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4347,15 +4347,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -4374,11 +4374,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -4401,23 +4401,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4437,11 +4437,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -4456,11 +4456,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -4477,7 +4477,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -4500,13 +4500,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-track-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-track-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -4518,27 +4518,27 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4556,7 +4556,7 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, @@ -4573,7 +4573,7 @@ Map { "stringtuning" => Map { "isstandard" => true, "name" => "Guitar Standard Tuning", - "tunings" => Array [ + "tunings" => [ 64, 59, 55, @@ -4595,13 +4595,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -4621,25 +4621,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -4647,7 +4647,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4665,7 +4665,7 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -4680,15 +4680,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -4704,7 +4704,7 @@ Map { Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -4720,11 +4720,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -4737,7 +4737,7 @@ Map { Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -4762,13 +4762,13 @@ Map { } `; -exports[`AlphaTexImporterTest bar-meta-interweaving with-previous-bars meta-voice-voice 1`] = ` +exports[`AlphaTexImporterTest > bar-meta-interweaving > with-previous-bars > meta-voice-voice 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -4788,25 +4788,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -4814,7 +4814,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -4832,7 +4832,7 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -4845,7 +4845,7 @@ Map { Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -4860,15 +4860,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -4884,7 +4884,7 @@ Map { Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -4897,7 +4897,7 @@ Map { Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -4913,11 +4913,11 @@ Map { "__kind" => "Bar", "id" => 2, "clef" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -4930,7 +4930,7 @@ Map { Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -4943,7 +4943,7 @@ Map { Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -4968,13 +4968,13 @@ Map { } `; -exports[`AlphaTexImporterTest barlines 1`] = ` +exports[`AlphaTexImporterTest > barlines 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -4990,26 +4990,26 @@ Map { "start" => 3840, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -5031,11 +5031,11 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -5054,15 +5054,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -5079,11 +5079,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -5111,15 +5111,15 @@ Map { } `; -exports[`AlphaTexImporterTest custom-beaming 1`] = ` +exports[`AlphaTexImporterTest > custom-beaming 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 2, 2, 2, @@ -5127,7 +5127,7 @@ Map { ], }, }, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -5146,7 +5146,7 @@ Map { "__kind" => "MasterBar", "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 4, 4, ], @@ -5155,25 +5155,25 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -5182,7 +5182,7 @@ Map { }, ], "duration" => 8, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -5198,7 +5198,7 @@ Map { Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -5215,7 +5215,7 @@ Map { Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -5232,7 +5232,7 @@ Map { Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -5249,7 +5249,7 @@ Map { Map { "__kind" => "Beat", "id" => 4, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 4, @@ -5266,7 +5266,7 @@ Map { Map { "__kind" => "Beat", "id" => 5, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 5, @@ -5283,7 +5283,7 @@ Map { Map { "__kind" => "Beat", "id" => 6, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 6, @@ -5300,7 +5300,7 @@ Map { Map { "__kind" => "Beat", "id" => 7, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 7, @@ -5321,15 +5321,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 8, @@ -5344,7 +5344,7 @@ Map { Map { "__kind" => "Beat", "id" => 9, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 9, @@ -5361,7 +5361,7 @@ Map { Map { "__kind" => "Beat", "id" => 10, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 10, @@ -5378,7 +5378,7 @@ Map { Map { "__kind" => "Beat", "id" => 11, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 11, @@ -5395,7 +5395,7 @@ Map { Map { "__kind" => "Beat", "id" => 12, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 12, @@ -5412,7 +5412,7 @@ Map { Map { "__kind" => "Beat", "id" => 13, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 13, @@ -5429,7 +5429,7 @@ Map { Map { "__kind" => "Beat", "id" => 14, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 14, @@ -5446,7 +5446,7 @@ Map { Map { "__kind" => "Beat", "id" => 15, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 15, @@ -5467,15 +5467,15 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 16, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 16, @@ -5490,7 +5490,7 @@ Map { Map { "__kind" => "Beat", "id" => 17, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 17, @@ -5507,7 +5507,7 @@ Map { Map { "__kind" => "Beat", "id" => 18, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 18, @@ -5524,7 +5524,7 @@ Map { Map { "__kind" => "Beat", "id" => 19, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 19, @@ -5541,7 +5541,7 @@ Map { Map { "__kind" => "Beat", "id" => 20, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 20, @@ -5558,7 +5558,7 @@ Map { Map { "__kind" => "Beat", "id" => 21, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 21, @@ -5575,7 +5575,7 @@ Map { Map { "__kind" => "Beat", "id" => 22, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 22, @@ -5592,7 +5592,7 @@ Map { Map { "__kind" => "Beat", "id" => 23, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 23, @@ -5609,7 +5609,7 @@ Map { Map { "__kind" => "Beat", "id" => 24, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 24, @@ -5626,7 +5626,7 @@ Map { Map { "__kind" => "Beat", "id" => 25, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 25, @@ -5643,7 +5643,7 @@ Map { Map { "__kind" => "Beat", "id" => 26, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 26, @@ -5660,7 +5660,7 @@ Map { Map { "__kind" => "Beat", "id" => 27, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 27, @@ -5677,7 +5677,7 @@ Map { Map { "__kind" => "Beat", "id" => 28, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 28, @@ -5694,7 +5694,7 @@ Map { Map { "__kind" => "Beat", "id" => 29, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 29, @@ -5711,7 +5711,7 @@ Map { Map { "__kind" => "Beat", "id" => 30, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 30, @@ -5728,7 +5728,7 @@ Map { Map { "__kind" => "Beat", "id" => 31, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 31, @@ -5759,31 +5759,12 @@ Map { } `; -exports[`AlphaTexImporterTest errors at209 accidentalmode: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > articulation > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 accidentalmode: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > articulation > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 accidentalmode: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected accidental mode value 'invalid', expected: auto,explicit", - "start" => Map { - "col" => 21, - "line" => 1, - "offset" => 20, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 articulation: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 articulation: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 articulation: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > articulation > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -5797,98 +5778,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 bar optional: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > beat tuplet > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 bar optional: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > beat tuplet > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 bar optional: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected additional value 'Number', expected: required(String|Ident),optional(String|Ident)", - "start" => Map { - "col" => 18, - "line" => 1, - "offset" => 17, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 bar required: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bar required: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bar required: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected required value 'String', expected: required(Number)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 barre: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 barre: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 barre: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected additional value 'Ident', expected: required(Number),optional(String|Ident)", - "start" => Map { - "col" => 21, - "line" => 1, - "offset" => 20, - }, - }, - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected barre shape value 'invalid', expected: none,full,half", - "start" => Map { - "col" => 21, - "line" => 1, - "offset" => 20, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 beam: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 beam: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 beam: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected beam value 'invalid', expected: invert,up,down,auto,split,merge,splitsecondary", - "start" => Map { - "col" => 19, - "line" => 1, - "offset" => 18, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 beat tuplet: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 beat tuplet: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 beat tuplet: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > beat tuplet > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -5902,69 +5797,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 bendstyle: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > duration tuplet > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 bendstyle: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > duration tuplet > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 bendstyle: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected bend style value 'invalid', expected: default,gradual,fast", - "start" => Map { - "col" => 19, - "line" => 1, - "offset" => 18, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 bendtype: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bendtype: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bendtype: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected bend type value 'invalid', expected: none,custom,bend,release,bendrelease,hold,prebend,prebendbend,prebendrelease", - "start" => Map { - "col" => 14, - "line" => 1, - "offset" => 13, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 bracketextendmode: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bracketextendmode: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 bracketextendmode: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected bracket extend mode value 'invalid', expected: nobrackets,groupstaves,groupsimilarinstruments", - "start" => Map { - "col" => 27, - "line" => 1, - "offset" => 26, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 duration tuplet: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 duration tuplet: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 duration tuplet: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > duration tuplet > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -5978,279 +5816,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 dynamic: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 dynamic: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 dynamic: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected dynamic value 'invalid', expected: ppp,pp,p,mp,mf,f,ff,fff,pppp,ppppp,pppppp,ffff,fffff,ffffff,sf,sfp,sfpp,fp,rf,rfz,sfz,sffz,fz,n,pf,sfzp", - "start" => Map { - "col" => 15, - "line" => 1, - "offset" => 14, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 fermata: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 fermata: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 fermata: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected additional value 'Ident', expected: optional(String|Ident),optional(Number)", - "start" => Map { - "col" => 21, - "line" => 1, - "offset" => 20, - }, - }, - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected fermata value 'invalid', expected: short,medium,long", - "start" => Map { - "col" => 21, - "line" => 1, - "offset" => 20, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknamemode: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknamemode: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknamemode: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name mode value 'invalid', expected: fullname,shortname", - "start" => Map { - "col" => 34, - "line" => 1, - "offset" => 33, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknameorientation: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknameorientation: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 firstsystemtracknameorientation: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name orientation value 'invalid', expected: horizontal,vertical", - "start" => Map { - "col" => 41, - "line" => 1, - "offset" => 40, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 gracetype: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 gracetype: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 gracetype: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected additional value 'Ident', expected: optional(String|Ident)", - "start" => Map { - "col" => 16, - "line" => 1, - "offset" => 15, - }, - }, - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected whammy style value 'invalid', expected: ob,b,bb", - "start" => Map { - "col" => 16, - "line" => 1, - "offset" => 15, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 multitracktracknamepolicy: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > textalign > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 multitracktracknamepolicy: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > textalign > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 multitracktracknamepolicy: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name policy value 'invalid', expected: hidden,firstsystem,allsystems", - "start" => Map { - "col" => 35, - "line" => 1, - "offset" => 34, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknamemode: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknamemode: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknamemode: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name mode value 'invalid', expected: fullname,shortname", - "start" => Map { - "col" => 35, - "line" => 1, - "offset" => 34, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknameorientation: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknameorientation: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 othersystemstracknameorientation: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name orientation value 'invalid', expected: horizontal,vertical", - "start" => Map { - "col" => 41, - "line" => 1, - "offset" => 40, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 ottava: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 ottava: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 ottava: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected ottava value 'invalid', expected: 15ma,8va,regular,8vb,15mb", - "start" => Map { - "col" => 15, - "line" => 1, - "offset" => 14, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 rasg: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 rasg: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 rasg: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected rasgueado pattern value 'invalid', expected: none,ii,mi,miitriplet,miianapaest,pmptriplet,pmpanapaest,peitriplet,peianapaest,paitriplet,paianapaest,amitriplet,amianapaest,ppp,amii,amip,eami,eamii,peami", - "start" => Map { - "col" => 17, - "line" => 1, - "offset" => 16, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 score optional: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 score optional: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 score optional: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected additional value 'Number', expected: required(String|Ident),optional(String),optional(String|Ident)", - "start" => Map { - "col" => 18, - "line" => 1, - "offset" => 17, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 score required: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 score required: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 score required: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected required value 'Number', expected: required(String|Ident)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 singletracktracknamepolicy: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 singletracktracknamepolicy: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 singletracktracknamepolicy: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected track name policy value 'invalid', expected: hidden,firstsystem,allsystems", - "start" => Map { - "col" => 36, - "line" => 1, - "offset" => 35, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 textalign: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 textalign: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 textalign: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > textalign > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6264,12 +5835,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 tremolo speed: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > tremolo speed > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 tremolo speed: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > tremolo speed > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 tremolo speed: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > tremolo speed > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6283,12 +5854,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 trill: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > trill > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 trill: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > trill > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 trill: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > trill > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6302,12 +5873,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 tuning: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > tuning > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 tuning: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at209 > tuning > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at209 tuning: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at209 > tuning > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6321,105 +5892,10 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at209 whammybarstyle: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 whammybarstyle: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 whammybarstyle: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected whammy style value 'invalid', expected: default,gradual,fast", - "start" => Map { - "col" => 20, - "line" => 1, - "offset" => 19, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at209 whammybartype: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 whammybartype: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at209 whammybartype: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 209, - "severity" => 2, - "message" => "Unexpected whammy type value 'invalid', expected: none,custom,dive,dip,hold,predive,predivedive", - "start" => Map { - "col" => 15, - "line" => 1, - "offset" => 14, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at210 bar empty: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 bar empty: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 bar empty: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 210, - "severity" => 2, - "message" => "Missing arguments. Expected following arguments: required(Number)", - "start" => Map { - "col" => 9, - "line" => 1, - "offset" => 8, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at210 bar missing: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 bar missing: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 bar missing: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 210, - "severity" => 2, - "message" => "Missing arguments. Expected following arguments: required(Number)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at210 score empty: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 score empty: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexImporterTest errors at210 score empty: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 210, - "severity" => 2, - "message" => "Missing arguments. Expected following arguments: required(String|Ident)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; - -exports[`AlphaTexImporterTest errors at219 accidentalmode: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > accidentalmode > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 accidentalmode: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > accidentalmode > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6434,14 +5910,14 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 accidentalmode: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > accidentalmode > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar empty: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > bar empty > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6457,12 +5933,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 bar missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar missing: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar missing > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar missing: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > bar missing > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6478,18 +5954,18 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 bar optional: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar optional > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar optional: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar optional > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar optional: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar optional > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar required: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar required > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar required: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bar required > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bar required: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > bar required > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6504,12 +5980,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 barre: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > barre > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 barre: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > barre > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 barre: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > barre > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6523,10 +5999,10 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 beam: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > beam > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 beam: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > beam > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6551,24 +6027,24 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 beam: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > beam > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendstyle: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendstyle > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendstyle: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendstyle > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendstyle: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendstyle > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendtype: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendtype > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendtype: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendtype > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bendtype: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bendtype > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bracketextendmode: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bracketextendmode > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 bracketextendmode: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > bracketextendmode > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6583,12 +6059,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 bracketextendmode: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > bracketextendmode > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 dynamic: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > dynamic > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 dynamic: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > dynamic > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6613,14 +6089,14 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 dynamic: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > dynamic > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 fermata: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > fermata > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 fermata: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > fermata > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 fermata: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > fermata > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6635,10 +6111,10 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknamemode: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknamemode > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknamemode: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknamemode > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6653,12 +6129,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknamemode: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknamemode > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknameorientation: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknameorientation > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknameorientation: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknameorientation > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6673,14 +6149,14 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 firstsystemtracknameorientation: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > firstsystemtracknameorientation > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 gracetype: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > gracetype > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 gracetype: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > gracetype > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 gracetype: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > gracetype > semantic-diagnostics 1`] = ` +[ Map { "code" => 209, "severity" => 2, @@ -6694,10 +6170,10 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 multitracktracknamepolicy: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > multitracktracknamepolicy > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 multitracktracknamepolicy: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > multitracktracknamepolicy > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6712,12 +6188,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 multitracktracknamepolicy: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > multitracktracknamepolicy > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 othersystemstracknamemode: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknamemode > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 othersystemstracknamemode: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknamemode > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6732,12 +6208,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 othersystemstracknamemode: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknamemode > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 othersystemstracknameorientation: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknameorientation > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 othersystemstracknameorientation: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknameorientation > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6752,12 +6228,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 othersystemstracknameorientation: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > othersystemstracknameorientation > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 ottava: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > ottava > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 ottava: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > ottava > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6782,12 +6258,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 ottava: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > ottava > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 rasg: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > rasg > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 rasg: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > rasg > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6812,14 +6288,14 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 rasg: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > rasg > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score empty: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > score empty > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6834,18 +6310,18 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 score optional: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score optional > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score optional: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score optional > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score optional: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score optional > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score required: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score required > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score required: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > score required > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 score required: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > score required > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6860,10 +6336,10 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 singletracktracknamepolicy: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > singletracktracknamepolicy > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 singletracktracknamepolicy: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > singletracktracknamepolicy > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -6878,12 +6354,12 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 singletracktracknamepolicy: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > singletracktracknamepolicy > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 whammybarstyle: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > whammybarstyle > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 whammybarstyle: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexImporterTest > errors > at219 > whammybarstyle > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, @@ -6897,21 +6373,21 @@ Array [ ] `; -exports[`AlphaTexImporterTest errors at219 whammybarstyle: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > whammybarstyle > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 whammybartype: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > whammybartype > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 whammybartype: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > whammybartype > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest errors at219 whammybartype: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexImporterTest > errors > at219 > whammybartype > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexImporterTest sync 1`] = ` +exports[`AlphaTexImporterTest > sync 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -6921,7 +6397,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -6950,7 +6426,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -6973,7 +6449,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7012,7 +6488,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7032,17 +6508,17 @@ Map { } `; -exports[`AlphaTexImporterTest sync-expect-dot 1`] = ` +exports[`AlphaTexImporterTest > sync-expect-dot 1`] = ` Map { "__kind" => "Score", "artist" => "J.S. Bach (1685-1750)", "copyright" => "Public Domain", "title" => "Prelude in D Minor", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "timesignaturenumerator" => 3, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -7052,7 +6528,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7082,7 +6558,7 @@ Map { Map { "__kind" => "MasterBar", "timesignaturenumerator" => 3, - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7101,7 +6577,7 @@ Map { Map { "__kind" => "MasterBar", "timesignaturenumerator" => 3, - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7120,7 +6596,7 @@ Map { Map { "__kind" => "MasterBar", "timesignaturenumerator" => 3, - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -7140,139 +6616,139 @@ Map { } `; -exports[`AlphaTexImporterTest tremolos buzzroll-default1 1`] = ` +exports[`AlphaTexImporterTest > tremolos > buzzroll-default1 1`] = ` Map { "marks" => 1, "style" => 1, } `; -exports[`AlphaTexImporterTest tremolos buzzroll-default2 1`] = ` +exports[`AlphaTexImporterTest > tremolos > buzzroll-default2 1`] = ` Map { "marks" => 2, "style" => 1, } `; -exports[`AlphaTexImporterTest tremolos buzzroll-default3 1`] = ` +exports[`AlphaTexImporterTest > tremolos > buzzroll-default3 1`] = ` Map { "marks" => 3, "style" => 1, } `; -exports[`AlphaTexImporterTest tremolos buzzroll-default4 1`] = ` +exports[`AlphaTexImporterTest > tremolos > buzzroll-default4 1`] = ` Map { "marks" => 4, "style" => 1, } `; -exports[`AlphaTexImporterTest tremolos buzzroll-default5 1`] = ` +exports[`AlphaTexImporterTest > tremolos > buzzroll-default5 1`] = ` Map { "marks" => 5, "style" => 1, } `; -exports[`AlphaTexImporterTest tremolos tremolo-default1 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo-default1 1`] = ` Map { "marks" => 1, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo-default2 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo-default2 1`] = ` Map { "marks" => 2, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo-default3 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo-default3 1`] = ` Map { "marks" => 3, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo-default4 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo-default4 1`] = ` Map { "marks" => 4, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo-default5 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo-default5 1`] = ` Map { "marks" => 5, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo1 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo1 1`] = ` Map { "marks" => 1, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo2 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo2 1`] = ` Map { "marks" => 2, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo3 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo3 1`] = ` Map { "marks" => 3, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo4 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo4 1`] = ` Map { "marks" => 4, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo5 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo5 1`] = ` Map { "marks" => 5, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo8 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo8 1`] = ` Map { "marks" => 1, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo16 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo16 1`] = ` Map { "marks" => 2, "style" => 0, } `; -exports[`AlphaTexImporterTest tremolos tremolo32 1`] = ` +exports[`AlphaTexImporterTest > tremolos > tremolo32 1`] = ` Map { "marks" => 3, "style" => 0, } `; -exports[`AlphaTexImporterTest voice-mode barWise 1`] = ` +exports[`AlphaTexImporterTest > voice-mode > barWise 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -7288,25 +6764,25 @@ Map { "start" => 3840, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -7314,7 +6790,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -7332,11 +6808,11 @@ Map { Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -7354,15 +6830,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -7378,11 +6854,11 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -7410,13 +6886,13 @@ Map { } `; -exports[`AlphaTexImporterTest voice-mode default 1`] = ` +exports[`AlphaTexImporterTest > voice-mode > default 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -7432,25 +6908,25 @@ Map { "start" => 3840, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -7458,7 +6934,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -7476,11 +6952,11 @@ Map { Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -7498,15 +6974,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -7522,11 +6998,11 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -7554,13 +7030,13 @@ Map { } `; -exports[`AlphaTexImporterTest voice-mode staffWise 1`] = ` +exports[`AlphaTexImporterTest > voice-mode > staffWise 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -7576,25 +7052,25 @@ Map { "start" => 3840, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -7602,7 +7078,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -7620,11 +7096,11 @@ Map { Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -7642,15 +7118,15 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -7666,11 +7142,11 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, diff --git a/packages/alphatab/test/importer/__snapshots__/AlphaTexLexer.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/AlphaTexLexer.test.ts.snap index 6bf901c10..b4f8b8d62 100644 --- a/packages/alphatab/test/importer/__snapshots__/AlphaTexLexer.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/AlphaTexLexer.test.ts.snap @@ -1,57 +1,57 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AlphaTexLexerTest basic-tokens 1`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 1`] = ` +[ Dot (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 2`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 2`] = ` +[ Colon (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 3`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 3`] = ` +[ LParen (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 4`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 4`] = ` +[ RParen (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 5`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 5`] = ` +[ LBrace (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 6`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 6`] = ` +[ RBrace (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 7`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 7`] = ` +[ Pipe (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest basic-tokens 8`] = ` -Array [ +exports[`AlphaTexLexerTest > basic-tokens 8`] = ` +[ Asterisk (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest errors at001 1`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at001 1`] = `[]`; -exports[`AlphaTexLexerTest errors at001 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at001 2`] = ` +[ Map { "code" => 1, "severity" => 2, @@ -65,14 +65,14 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at002 1`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at002 1`] = ` +[ String "Test" (1,3) -> (1,8), ] `; -exports[`AlphaTexLexerTest errors at002 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at002 2`] = ` +[ Map { "code" => 2, "severity" => 2, @@ -86,14 +86,14 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at003 1`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at003 1`] = `[]`; -exports[`AlphaTexLexerTest errors at003 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at003 2`] = ` +[ Map { "code" => 3, "severity" => 2, - "message" => "Unexpected end of file. Need 4 hex characters on a \\\\uXXXX escape sequence", + "message" => "Unexpected end of file. Need 4 hex characters on a \\uXXXX escape sequence", "start" => Map { "col" => 7, "line" => 1, @@ -103,14 +103,14 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at004 1`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at004 1`] = `[]`; -exports[`AlphaTexLexerTest errors at004 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at004 2`] = ` +[ Map { "code" => 4, "severity" => 2, - "message" => "Invalid unicode value. Need 4 hex characters on a \\\\uXXXX escape sequence.", + "message" => "Invalid unicode value. Need 4 hex characters on a \\uXXXX escape sequence.", "start" => Map { "col" => 7, "line" => 1, @@ -120,14 +120,14 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at005 1`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at005 1`] = `[]`; -exports[`AlphaTexLexerTest errors at005 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at005 2`] = ` +[ Map { "code" => 5, "severity" => 2, - "message" => "Unsupported escape sequence. Expected '\\\\n', '\\\\r', '\\\\t', or '\\\\uXXXX' but found '\\\\b'.", + "message" => "Unsupported escape sequence. Expected '\\n', '\\r', '\\t', or '\\uXXXX' but found '\\b'.", "start" => Map { "col" => 3, "line" => 1, @@ -137,10 +137,10 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at006 1`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at006 1`] = `[]`; -exports[`AlphaTexLexerTest errors at006 2`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at006 2`] = ` +[ Map { "code" => 6, "severity" => 2, @@ -154,10 +154,10 @@ Array [ ] `; -exports[`AlphaTexLexerTest errors at006 3`] = `Array []`; +exports[`AlphaTexLexerTest > errors > at006 3`] = `[]`; -exports[`AlphaTexLexerTest errors at006 4`] = ` -Array [ +exports[`AlphaTexLexerTest > errors > at006 4`] = ` +[ Map { "code" => 6, "severity" => 2, @@ -171,96 +171,96 @@ Array [ ] `; -exports[`AlphaTexLexerTest floats 1`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 1`] = ` +[ Number "1.1" (1,1) -> (1,4), ] `; -exports[`AlphaTexLexerTest floats 2`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 2`] = ` +[ Number "11.22" (1,1) -> (1,6), Number "33.44" (1,7) -> (1,12), ] `; -exports[`AlphaTexLexerTest floats 3`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 3`] = ` +[ Number "1.1" (1,1) -> (1,4), Dot (1,4) -> (1,5), Number "4" (1,5) -> (1,6), ] `; -exports[`AlphaTexLexerTest floats 4`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 4`] = ` +[ Number "1.1" (1,1) -> (1,4), Dot (1,5) -> (1,6), Number "4" (1,6) -> (1,7), ] `; -exports[`AlphaTexLexerTest floats 5`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 5`] = ` +[ Number "1" (1,1) -> (1,2), Dot (1,3) -> (1,4), Number "1.4" (1,4) -> (1,7), ] `; -exports[`AlphaTexLexerTest floats 6`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 6`] = ` +[ Number "-1.1" (1,1) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 7`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 7`] = ` +[ Ident "-" (1,1) -> (1,2), Dot (1,2) -> (1,3), Number "1" (1,3) -> (1,4), ] `; -exports[`AlphaTexLexerTest floats 8`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 8`] = ` +[ Number "1.1" (1,1) -> (1,4), LParen (1,4) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 9`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 9`] = ` +[ Number "1.1" (1,1) -> (1,4), LBrace (1,4) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 10`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 10`] = ` +[ Number "1.1" (1,1) -> (1,4), Pipe (1,4) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 11`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 11`] = ` +[ Number "1" (1,1) -> (1,2), Dot (1,2) -> (1,3), Ident "1a" (1,3) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 12`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 12`] = ` +[ Ident "1a" (1,1) -> (1,3), Dot (1,3) -> (1,4), Number "1" (1,4) -> (1,5), ] `; -exports[`AlphaTexLexerTest floats 13`] = ` -Array [ +exports[`AlphaTexLexerTest > floats 13`] = ` +[ Number "1.1" (1,1) -> (1,4), Tag "test" (1,4) -> (1,9) { prefix: Backslash (1,4) -> (1,5), @@ -269,79 +269,79 @@ Array [ ] `; -exports[`AlphaTexLexerTest identifiers 1`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 1`] = ` +[ Ident "true" (1,1) -> (1,5), ] `; -exports[`AlphaTexLexerTest identifiers 2`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 2`] = ` +[ Ident "false" (1,1) -> (1,6), ] `; -exports[`AlphaTexLexerTest identifiers 3`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 3`] = ` +[ Ident "HelloWorld" (1,1) -> (1,11), ] `; -exports[`AlphaTexLexerTest identifiers 4`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 4`] = ` +[ Ident "C4" (1,1) -> (1,3), ] `; -exports[`AlphaTexLexerTest identifiers 5`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 5`] = ` +[ Ident "C#4" (1,1) -> (1,4), ] `; -exports[`AlphaTexLexerTest identifiers 6`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 6`] = ` +[ Ident "Cb4" (1,1) -> (1,4), ] `; -exports[`AlphaTexLexerTest identifiers 7`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 7`] = ` +[ Ident "electricpiano1" (1,1) -> (1,15), ] `; -exports[`AlphaTexLexerTest identifiers 8`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 8`] = ` +[ Ident "Unicodeöäü" (1,1) -> (1,11), Ident "Unicode😸" (1,12) -> (1,20), Ident "Utf16🤘🏻" (1,21) -> (1,28), ] `; -exports[`AlphaTexLexerTest identifiers 9`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 9`] = ` +[ Ident "dashed-identifier" (1,1) -> (1,18), ] `; -exports[`AlphaTexLexerTest identifiers 10`] = ` -Array [ +exports[`AlphaTexLexerTest > identifiers 10`] = ` +[ Ident "HelloWorld" (1,1) -> (1,11), Ident "Multiple" (1,12) -> (1,20), Ident "Identifiers" (1,21) -> (1,32), ] `; -exports[`AlphaTexLexerTest leading-comments 1`] = ` -Array [ +exports[`AlphaTexLexerTest > leading-comments 1`] = ` +[ Ident "true" (3,9) -> (3,13) { - leadingComments: Array [ + leadingComments: [ "// Single", ], }, Ident "false" (7,9) -> (7,14) { - leadingComments: Array [ + leadingComments: [ "/** Multi **/", "/** Multi2 **/", "// Single", @@ -350,8 +350,8 @@ Array [ ] `; -exports[`AlphaTexLexerTest meta-command 1`] = ` -Array [ +exports[`AlphaTexLexerTest > meta-command 1`] = ` +[ Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), @@ -359,8 +359,8 @@ Array [ ] `; -exports[`AlphaTexLexerTest meta-command 2`] = ` -Array [ +exports[`AlphaTexLexerTest > meta-command 2`] = ` +[ Tag "double" (1,1) -> (1,9) { prefix: DoubleBackslash (1,1) -> (1,3), tag: Ident "double" (1,3) -> (1,9), @@ -368,8 +368,8 @@ Array [ ] `; -exports[`AlphaTexLexerTest meta-command 3`] = ` -Array [ +exports[`AlphaTexLexerTest > meta-command 3`] = ` +[ Tag "withNumber123" (1,1) -> (1,15) { prefix: Backslash (1,1) -> (1,2), tag: Ident "withNumber123" (1,2) -> (1,15), @@ -377,8 +377,8 @@ Array [ ] `; -exports[`AlphaTexLexerTest meta-command 4`] = ` -Array [ +exports[`AlphaTexLexerTest > meta-command 4`] = ` +[ Tag "withUnicode😼" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), tag: Ident "withUnicode😼" (1,2) -> (1,14), @@ -386,8 +386,8 @@ Array [ ] `; -exports[`AlphaTexLexerTest meta-command 5`] = ` -Array [ +exports[`AlphaTexLexerTest > meta-command 5`] = ` +[ Tag "withUnicode😼" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), tag: Ident "withUnicode😼" (1,2) -> (1,14), @@ -400,122 +400,122 @@ Array [ ] `; -exports[`AlphaTexLexerTest numbers 1`] = ` -Array [ +exports[`AlphaTexLexerTest > numbers 1`] = ` +[ Number "1" (1,1) -> (1,2), ] `; -exports[`AlphaTexLexerTest numbers 2`] = ` -Array [ +exports[`AlphaTexLexerTest > numbers 2`] = ` +[ Number "1234" (1,1) -> (1,5), ] `; -exports[`AlphaTexLexerTest numbers 3`] = ` -Array [ +exports[`AlphaTexLexerTest > numbers 3`] = ` +[ Number "-1" (1,1) -> (1,3), ] `; -exports[`AlphaTexLexerTest numbers 4`] = ` -Array [ +exports[`AlphaTexLexerTest > numbers 4`] = ` +[ Number "-1234" (1,1) -> (1,6), ] `; -exports[`AlphaTexLexerTest numbers 5`] = ` -Array [ +exports[`AlphaTexLexerTest > numbers 5`] = ` +[ Number "1234" (1,1) -> (1,5), Number "5678" (1,6) -> (1,10), ] `; -exports[`AlphaTexLexerTest strings 1`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 1`] = ` +[ String "Double Quoted" (1,1) -> (1,15), ] `; -exports[`AlphaTexLexerTest strings 2`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 2`] = ` +[ String "Single Quoted" (1,1) -> (1,15), ] `; -exports[`AlphaTexLexerTest strings 3`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 3`] = ` +[ String "Multiple" (1,1) -> (1,10), String "Strings" (1,12) -> (1,20), ] `; -exports[`AlphaTexLexerTest strings 4`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 4`] = ` +[ String "Double \\"Quoted\\"" (1,1) -> (1,19), ] `; -exports[`AlphaTexLexerTest strings 5`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 5`] = ` +[ String "Single 'Quoted'" (1,1) -> (1,19), ] `; -exports[`AlphaTexLexerTest strings 6`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 6`] = ` +[ String "\\r\\n\\t" (1,1) -> (1,8), ] `; -exports[`AlphaTexLexerTest strings 7`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 7`] = ` +[ String "\\r\\n\\t" (1,1) -> (1,8), ] `; -exports[`AlphaTexLexerTest strings 8`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 8`] = ` +[ String "😸" (1,1) -> (1,14), ] `; -exports[`AlphaTexLexerTest strings 9`] = ` -Array [ +exports[`AlphaTexLexerTest > strings 9`] = ` +[ String "😸🤘🏻" (1,1) -> (1,5), ] `; -exports[`AlphaTexLexerTest trailing-comments 1`] = ` -Array [ +exports[`AlphaTexLexerTest > trailing-comments 1`] = ` +[ Ident "true" (2,9) -> (2,13) { - trailingComments: Array [ + trailingComments: [ "// Single After", ], }, Ident "false" (3,9) -> (3,14) { - trailingComments: Array [ + trailingComments: [ "/** Multi After **/", ], }, Ident "true" (4,22) -> (4,26) { - leadingComments: Array [ + leadingComments: [ "/** before **/", ], - trailingComments: Array [ + trailingComments: [ "/** middle **/", ], }, Ident "false" (4,40) -> (4,45) { - trailingComments: Array [ + trailingComments: [ "// after", ], }, ] `; -exports[`AlphaTexLexerTest whitespace 1`] = ` -Array [ +exports[`AlphaTexLexerTest > whitespace 1`] = ` +[ Dot (1,3) -> (1,4), Dot (2,7) -> (2,8), ] diff --git a/packages/alphatab/test/importer/__snapshots__/AlphaTexParameter.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/AlphaTexParameter.test.ts.snap index 58e202661..395d10e4a 100644 --- a/packages/alphatab/test/importer/__snapshots__/AlphaTexParameter.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/AlphaTexParameter.test.ts.snap @@ -1,10 +1,16 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AlphaTexParameterTests handler-validation metadata empty signature empty 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > empty signature > empty > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParameterTests > handler-validation > metadata > empty signature > empty > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParameterTests > handler-validation > metadata > empty signature > empty > semantic-diagnostics 1`] = `[]`; + +exports[`AlphaTexParameterTests > handler-validation > metadata > empty signature > empty 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,9) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -12,18 +18,18 @@ Score (1,1) -> (1,12) { }, arguments: Arguments (1,7) -> (1,9) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,8) -> (1,9), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, }, ], - beats: Array [ + beats: [ Beat (1,10) -> (1,12) { notes: NoteList (1,10) -> (1,12) { - notes: Array [ + notes: [ Note (1,10) -> (1,12) { noteValue: Ident "C4" (1,10) -> (1,12), }, @@ -36,17 +42,17 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParameterTests handler-validation metadata empty signature empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > multi > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata empty signature empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > multi > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata empty signature empty: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > multi > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list multi 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > list > multi 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "systemslayout" (1,1) -> (1,15) { prefix: Backslash (1,1) -> (1,2), @@ -54,7 +60,7 @@ Score (1,1) -> (1,23) { }, arguments: Arguments (1,16) -> (1,23) { openParenthesis: LParen (1,16) -> (1,17), - arguments: Array [ + arguments: [ Number "3" (1,17) -> (1,18) { parameterIndices: Map { 0 => 0, @@ -72,7 +78,7 @@ Score (1,1) -> (1,23) { }, ], closeParenthesis: RParen (1,22) -> (1,23), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -83,17 +89,17 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests handler-validation metadata list multi: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > single > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list multi: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > single > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list multi: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > list > single > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list single 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > list > single 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,19) { tag: Tag "systemslayout" (1,1) -> (1,15) { prefix: Backslash (1,1) -> (1,2), @@ -101,7 +107,7 @@ Score (1,1) -> (1,19) { }, arguments: Arguments (1,16) -> (1,19) { openParenthesis: LParen (1,16) -> (1,17), - arguments: Array [ + arguments: [ Number "3" (1,17) -> (1,18) { parameterIndices: Map { 0 => 0, @@ -109,7 +115,7 @@ Score (1,1) -> (1,19) { }, ], closeParenthesis: RParen (1,18) -> (1,19), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -120,17 +126,17 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParameterTests handler-validation metadata list single: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > multi > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list single: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > multi > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata list single: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > multi > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads multi 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > multi 1`] = ` Score (1,1) -> (1,28) { - bars: Array [ + bars: [ Bar (1,1) -> (1,28) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,28) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -138,7 +144,7 @@ Score (1,1) -> (1,28) { }, arguments: Arguments (1,8) -> (1,28) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ String "Name" (1,9) -> (1,14) { parameterIndices: Map { 0 => 0, @@ -151,7 +157,7 @@ Score (1,1) -> (1,28) { }, ], closeParenthesis: RParen (1,27) -> (1,28), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -162,17 +168,17 @@ Score (1,1) -> (1,28) { } `; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads multi: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > none > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads multi: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > none > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads multi: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > none > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads none 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > none 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,10) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -180,9 +186,9 @@ Score (1,1) -> (1,10) { }, arguments: Arguments (1,8) -> (1,10) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,9) -> (1,10), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -193,17 +199,17 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads none: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > single > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads none: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > single > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads none: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > single > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads single 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > optional overloads > single 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,16) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -211,7 +217,7 @@ Score (1,1) -> (1,16) { }, arguments: Arguments (1,8) -> (1,16) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ String "Name" (1,9) -> (1,14) { parameterIndices: Map { 0 => 0, @@ -219,7 +225,7 @@ Score (1,1) -> (1,16) { }, ], closeParenthesis: RParen (1,15) -> (1,16), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -230,17 +236,17 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads single: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads single: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata optional overloads single: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload1 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload1 1`] = ` Score (1,1) -> (1,18) { - bars: Array [ + bars: [ Bar (1,1) -> (1,18) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,18) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), @@ -248,7 +254,7 @@ Score (1,1) -> (1,18) { }, arguments: Arguments (1,10) -> (1,18) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ String "Text" (1,11) -> (1,16) { parameterIndices: Map { 0 => 0, @@ -257,7 +263,7 @@ Score (1,1) -> (1,18) { }, ], closeParenthesis: RParen (1,17) -> (1,18), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -269,17 +275,17 @@ Score (1,1) -> (1,18) { } `; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload1: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload2 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > required overloads > overload2 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,1) -> (1,22) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,22) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), @@ -287,7 +293,7 @@ Score (1,1) -> (1,22) { }, arguments: Arguments (1,10) -> (1,22) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ String "T" (1,11) -> (1,13) { parameterIndices: Map { 0 => 0, @@ -301,7 +307,7 @@ Score (1,1) -> (1,22) { }, ], closeParenthesis: RParen (1,21) -> (1,22), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -312,17 +318,17 @@ Score (1,1) -> (1,22) { } `; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > correct > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > correct > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata required overloads overload2: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > correct > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload correct 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > correct 1`] = ` Score (1,1) -> (1,17) { - bars: Array [ + bars: [ Bar (1,1) -> (1,17) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -330,7 +336,7 @@ Score (1,1) -> (1,17) { }, arguments: Arguments (1,7) -> (1,14) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [ + arguments: [ Ident "Segno" (1,8) -> (1,13) { parameterIndices: Map { 0 => 0, @@ -338,16 +344,16 @@ Score (1,1) -> (1,17) { }, ], closeParenthesis: RParen (1,13) -> (1,14), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, }, ], - beats: Array [ + beats: [ Beat (1,15) -> (1,17) { notes: NoteList (1,15) -> (1,17) { - notes: Array [ + notes: [ Note (1,15) -> (1,17) { noteValue: Ident "C4" (1,15) -> (1,17), }, @@ -360,17 +366,31 @@ Score (1,1) -> (1,17) { } `; -exports[`AlphaTexParameterTests handler-validation metadata single overload correct: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload correct: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > missing > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload correct: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > missing > semantic-diagnostics 1`] = ` +[ + Map { + "code" => 219, + "severity" => 2, + "message" => "Error parsing arguments: no overload matched arguments (). Signatures: +(Ident|String)", + "start" => Map { + "col" => 9, + "line" => 1, + "offset" => 8, + }, + }, +] +`; -exports[`AlphaTexParameterTests handler-validation metadata single overload missing 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > missing 1`] = ` Score (1,1) -> (1,13) { - bars: Array [ + bars: [ Bar (1,1) -> (1,13) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,9) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -378,9 +398,9 @@ Score (1,1) -> (1,13) { }, arguments: Arguments (1,7) -> (1,9) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,8) -> (1,9), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -397,31 +417,31 @@ Score (1,1) -> (1,13) { } `; -exports[`AlphaTexParameterTests handler-validation metadata single overload missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload missing: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong identifier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload missing: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong identifier > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (). Signatures: + "message" => "Error parsing arguments: no overload matched arguments (Ident). Signatures: (Ident|String)", "start" => Map { - "col" => 9, + "col" => 11, "line" => 1, - "offset" => 8, + "offset" => 10, }, }, ] `; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong identifier 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong identifier 1`] = ` Score (1,1) -> (1,11) { - bars: Array [ + bars: [ Bar (1,1) -> (1,11) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,11) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -429,11 +449,11 @@ Score (1,1) -> (1,11) { }, arguments: Arguments (1,7) -> (1,11) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [ + arguments: [ Ident "C4" (1,8) -> (1,10), ], closeParenthesis: RParen (1,10) -> (1,11), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -444,31 +464,31 @@ Score (1,1) -> (1,11) { } `; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong identifier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong type > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong identifier: semantic-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong type > semantic-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (Ident). Signatures: + "message" => "Error parsing arguments: no overload matched arguments (Number). Signatures: (Ident|String)", "start" => Map { - "col" => 11, + "col" => 10, "line" => 1, - "offset" => 10, + "offset" => 9, }, }, ] `; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong type 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > single overload > wrong type 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,10) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -476,11 +496,11 @@ Score (1,1) -> (1,10) { }, arguments: Arguments (1,7) -> (1,10) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [ + arguments: [ Number "3" (1,8) -> (1,9), ], closeParenthesis: RParen (1,9) -> (1,10), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -491,31 +511,17 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload1-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong type: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload1-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata single overload wrong type: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (Number). Signatures: -(Ident|String)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload1-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload1-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload1-ident 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,25) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), @@ -523,7 +529,7 @@ Score (1,1) -> (1,25) { }, arguments: Arguments (1,15) -> (1,25) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Ident "defaults" (1,16) -> (1,24) { parameterIndices: Map { 0 => 0, @@ -532,7 +538,7 @@ Score (1,1) -> (1,25) { }, ], closeParenthesis: RParen (1,24) -> (1,25), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -544,17 +550,17 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload1-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload1-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload1-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-ident 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,21) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), @@ -562,7 +568,7 @@ Score (1,1) -> (1,21) { }, arguments: Arguments (1,15) -> (1,21) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Ident "A" (1,16) -> (1,17) { parameterIndices: Map { 1 => 0, @@ -575,7 +581,7 @@ Score (1,1) -> (1,21) { }, ], closeParenthesis: RParen (1,20) -> (1,21), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -586,17 +592,17 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > type overloads > overload2-string 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), @@ -604,7 +610,7 @@ Score (1,1) -> (1,23) { }, arguments: Arguments (1,15) -> (1,23) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ String "A" (1,16) -> (1,18) { parameterIndices: Map { 1 => 0, @@ -617,7 +623,7 @@ Score (1,1) -> (1,23) { }, ], closeParenthesis: RParen (1,22) -> (1,23), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -628,17 +634,17 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata type overloads overload2-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload1 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload1 1`] = ` Score (1,1) -> (1,13) { - bars: Array [ + bars: [ Bar (1,1) -> (1,13) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,13) { tag: Tag "ts" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), @@ -646,7 +652,7 @@ Score (1,1) -> (1,13) { }, arguments: Arguments (1,5) -> (1,13) { openParenthesis: LParen (1,5) -> (1,6), - arguments: Array [ + arguments: [ Ident "common" (1,6) -> (1,12) { parameterIndices: Map { 0 => 0, @@ -654,7 +660,7 @@ Score (1,1) -> (1,13) { }, ], closeParenthesis: RParen (1,12) -> (1,13), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -665,17 +671,17 @@ Score (1,1) -> (1,13) { } `; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload1: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload2 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > metadata > value overloads > overload2 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,10) { tag: Tag "ts" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), @@ -683,7 +689,7 @@ Score (1,1) -> (1,10) { }, arguments: Arguments (1,5) -> (1,10) { openParenthesis: LParen (1,5) -> (1,6), - arguments: Array [ + arguments: [ Number "3" (1,6) -> (1,7) { parameterIndices: Map { 1 => 0, @@ -696,7 +702,7 @@ Score (1,1) -> (1,10) { }, ], closeParenthesis: RParen (1,9) -> (1,10), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -707,30 +713,30 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > ambiguous > incomplete > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > ambiguous > incomplete > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation metadata value overloads overload2: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > ambiguous > incomplete > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props ambiguous incomplete 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > ambiguous > incomplete 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,14) { - notes: Array [ + notes: [ Note (1,1) -> (1,14) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,14) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,12) { property: Ident "tu" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,12) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Number "3" (1,10) -> (1,11) { parameterIndices: Map { 0 => 0, @@ -739,7 +745,7 @@ Score (1,1) -> (1,14) { }, ], closeParenthesis: RParen (1,11) -> (1,12), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -758,30 +764,30 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParameterTests handler-validation props ambiguous incomplete: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props ambiguous incomplete: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props ambiguous incomplete: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty1 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty1 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty1 1`] = ` Score (1,1) -> (1,9) { - bars: Array [ + bars: [ Bar (1,1) -> (1,9) { - beats: Array [ + beats: [ Beat (1,1) -> (1,9) { notes: NoteList (1,1) -> (1,9) { - notes: Array [ + notes: [ Note (1,1) -> (1,9) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,9) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,8) { property: Ident "v" (1,5) -> (1,6), properties: Arguments (1,6) -> (1,8) { openParenthesis: LParen (1,6) -> (1,7), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,7) -> (1,8), }, }, @@ -798,30 +804,30 @@ Score (1,1) -> (1,9) { } `; -exports[`AlphaTexParameterTests handler-validation props empty signature empty1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty1: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty2 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty2 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > empty signature > empty2 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,12) { - notes: Array [ + notes: [ Note (1,1) -> (1,12) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,12) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,8) { property: Ident "v" (1,5) -> (1,6), properties: Arguments (1,6) -> (1,8) { openParenthesis: LParen (1,6) -> (1,7), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,7) -> (1,8), }, }, @@ -841,30 +847,30 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParameterTests handler-validation props empty signature empty2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > correct > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > correct > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props empty signature empty2: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > correct > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload correct 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > correct 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,14) { - notes: Array [ + notes: [ Note (1,1) -> (1,14) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,14) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,13) { property: Ident "ad" (1,5) -> (1,7), properties: Arguments (1,8) -> (1,13) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ Number "100" (1,9) -> (1,12) { parameterIndices: Map { 0 => 0, @@ -872,7 +878,7 @@ Score (1,1) -> (1,14) { }, ], closeParenthesis: RParen (1,12) -> (1,13), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -890,32 +896,32 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParameterTests handler-validation props optional overload correct: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload correct: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > missing > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload correct: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > missing > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload missing 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > missing 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,10) { - notes: Array [ + notes: [ Note (1,1) -> (1,10) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,10) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,9) { property: Ident "ad" (1,5) -> (1,7), properties: Arguments (1,7) -> (1,9) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [], + arguments: [], closeParenthesis: RParen (1,8) -> (1,9), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -933,34 +939,48 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests handler-validation props optional overload missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload missing: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong identifier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload missing: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong identifier > semantic-diagnostics 1`] = ` +[ + Map { + "code" => 219, + "severity" => 2, + "message" => "Error parsing arguments: no overload matched arguments (Ident). Signatures: +(Ident|String)", + "start" => Map { + "col" => 19, + "line" => 1, + "offset" => 18, + }, + }, +] +`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong identifier 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong identifier 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - beats: Array [ + beats: [ Beat (1,1) -> (1,20) { notes: NoteList (1,1) -> (1,20) { - notes: Array [ + notes: [ Note (1,1) -> (1,20) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,20) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,19) { property: Ident "beam" (1,5) -> (1,9), properties: Arguments (1,10) -> (1,19) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Ident "invalid" (1,11) -> (1,18), ], closeParenthesis: RParen (1,18) -> (1,19), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -978,48 +998,34 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong identifier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong type > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong identifier: semantic-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (Ident). Signatures: -(Ident|String)", - "start" => Map { - "col" => 19, - "line" => 1, - "offset" => 18, - }, - }, -] -`; +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong type > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong type 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > optional overload > wrong type 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,14) { - notes: Array [ + notes: [ Note (1,1) -> (1,14) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,14) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,13) { property: Ident "ad" (1,5) -> (1,7), properties: Arguments (1,8) -> (1,13) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ String "1" (1,9) -> (1,11), ], closeParenthesis: RParen (1,12) -> (1,13), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -1037,17 +1043,17 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload1-number > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong type: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload1-number > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props optional overload wrong type: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload1-number > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload1-number 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload1-number 1`] = ` Score (1,1) -> (1,27) { - bars: Array [ + bars: [ Bar (1,1) -> (1,27) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,27) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1055,12 +1061,12 @@ Score (1,1) -> (1,27) { }, properties: Props (1,8) -> (1,27) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,25) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,25) { openParenthesis: LParen (1,21) -> (1,22), - arguments: Array [ + arguments: [ Number "20" (1,22) -> (1,24) { parameterIndices: Map { 0 => 0, @@ -1068,7 +1074,7 @@ Score (1,1) -> (1,27) { }, ], closeParenthesis: RParen (1,24) -> (1,25), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -1083,17 +1089,17 @@ Score (1,1) -> (1,27) { } `; -exports[`AlphaTexParameterTests handler-validation props type overloads overload1-number: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload1-number: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-identifier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload1-number: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-identifier > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-identifier 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-identifier 1`] = ` Score (1,1) -> (1,43) { - bars: Array [ + bars: [ Bar (1,1) -> (1,43) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,43) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1101,12 +1107,12 @@ Score (1,1) -> (1,43) { }, properties: Props (1,8) -> (1,43) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,41) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,41) { openParenthesis: LParen (1,21) -> (1,22), - arguments: Array [ + arguments: [ Ident "AcousticGrandPiano" (1,22) -> (1,40) { parameterIndices: Map { 1 => 0, @@ -1114,7 +1120,7 @@ Score (1,1) -> (1,43) { }, ], closeParenthesis: RParen (1,40) -> (1,41), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -1129,17 +1135,17 @@ Score (1,1) -> (1,43) { } `; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-identifier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-identifier: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload2-string 1`] = ` Score (1,1) -> (1,46) { - bars: Array [ + bars: [ Bar (1,1) -> (1,46) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,46) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1147,12 +1153,12 @@ Score (1,1) -> (1,46) { }, properties: Props (1,8) -> (1,46) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,45) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,45) { openParenthesis: LParen (1,21) -> (1,22), - arguments: Array [ + arguments: [ String "Acoustic Grand Piano" (1,22) -> (1,43) { parameterIndices: Map { 1 => 0, @@ -1160,7 +1166,7 @@ Score (1,1) -> (1,46) { }, ], closeParenthesis: RParen (1,44) -> (1,45), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -1175,17 +1181,17 @@ Score (1,1) -> (1,46) { } `; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload3-percussion > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload3-percussion > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload2-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload3-percussion > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload3-percussion 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > type overloads > overload3-percussion 1`] = ` Score (1,1) -> (1,35) { - bars: Array [ + bars: [ Bar (1,1) -> (1,35) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,35) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1193,12 +1199,12 @@ Score (1,1) -> (1,35) { }, properties: Props (1,8) -> (1,35) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,33) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,33) { openParenthesis: LParen (1,21) -> (1,22), - arguments: Array [ + arguments: [ Ident "percussion" (1,22) -> (1,32) { parameterIndices: Map { 1 => 0, @@ -1207,7 +1213,7 @@ Score (1,1) -> (1,35) { }, ], closeParenthesis: RParen (1,32) -> (1,33), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, 1, ], @@ -1223,30 +1229,30 @@ Score (1,1) -> (1,35) { } `; -exports[`AlphaTexParameterTests handler-validation props type overloads overload3-percussion: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload3-percussion: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props type overloads overload3-percussion: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload1 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload1 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload1 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,16) { notes: NoteList (1,1) -> (1,16) { - notes: Array [ + notes: [ Note (1,1) -> (1,16) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,16) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,14) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,14) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Number "0" (1,10) -> (1,11) { parameterIndices: Map { 0 => 0, @@ -1259,7 +1265,7 @@ Score (1,1) -> (1,16) { }, ], closeParenthesis: RParen (1,13) -> (1,14), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -1277,30 +1283,30 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload1: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-ident 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - beats: Array [ + beats: [ Beat (1,1) -> (1,23) { notes: NoteList (1,1) -> (1,23) { - notes: Array [ + notes: [ Note (1,1) -> (1,23) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,23) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,21) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,21) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Ident "custom" (1,10) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -1319,7 +1325,7 @@ Score (1,1) -> (1,23) { }, ], closeParenthesis: RParen (1,20) -> (1,21), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -1337,30 +1343,30 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload2-string 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - beats: Array [ + beats: [ Beat (1,1) -> (1,25) { notes: NoteList (1,1) -> (1,25) { - notes: Array [ + notes: [ Note (1,1) -> (1,25) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,25) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,23) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,23) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ String "custom" (1,10) -> (1,17) { parameterIndices: Map { 1 => 0, @@ -1379,7 +1385,7 @@ Score (1,1) -> (1,25) { }, ], closeParenthesis: RParen (1,22) -> (1,23), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], }, @@ -1397,30 +1403,30 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload2-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-ident 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { notes: NoteList (1,1) -> (1,24) { - notes: Array [ + notes: [ Note (1,1) -> (1,24) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,24) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,22) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,22) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Ident "gradual" (1,10) -> (1,17) { parameterIndices: Map { 2 => 0, @@ -1438,7 +1444,7 @@ Score (1,1) -> (1,24) { }, ], closeParenthesis: RParen (1,21) -> (1,22), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], }, @@ -1456,30 +1462,30 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload3-string 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - beats: Array [ + beats: [ Beat (1,1) -> (1,26) { notes: NoteList (1,1) -> (1,26) { - notes: Array [ + notes: [ Note (1,1) -> (1,26) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,26) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,24) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,24) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ String "gradual" (1,10) -> (1,18) { parameterIndices: Map { 2 => 0, @@ -1497,7 +1503,7 @@ Score (1,1) -> (1,26) { }, ], closeParenthesis: RParen (1,23) -> (1,24), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], }, @@ -1515,30 +1521,30 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload3-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-ident 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - beats: Array [ + beats: [ Beat (1,1) -> (1,31) { notes: NoteList (1,1) -> (1,31) { - notes: Array [ + notes: [ Note (1,1) -> (1,31) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,31) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,29) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,29) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Ident "custom" (1,10) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -1562,7 +1568,7 @@ Score (1,1) -> (1,31) { }, ], closeParenthesis: RParen (1,28) -> (1,29), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], }, @@ -1580,30 +1586,30 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > flat > overload4-string 1`] = ` Score (1,1) -> (1,35) { - bars: Array [ + bars: [ Bar (1,1) -> (1,35) { - beats: Array [ + beats: [ Beat (1,1) -> (1,35) { notes: NoteList (1,1) -> (1,35) { - notes: Array [ + notes: [ Note (1,1) -> (1,35) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,35) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,33) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,33) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ String "custom" (1,10) -> (1,17) { parameterIndices: Map { 1 => 0, @@ -1627,7 +1633,7 @@ Score (1,1) -> (1,35) { }, ], closeParenthesis: RParen (1,32) -> (1,33), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], }, @@ -1645,30 +1651,30 @@ Score (1,1) -> (1,35) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads flat overload4-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload1 > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload1 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload1 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,16) { notes: NoteList (1,1) -> (1,16) { - notes: Array [ + notes: [ Note (1,1) -> (1,16) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,16) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,14) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,14) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Number "0" (1,10) -> (1,11) { parameterIndices: Map { 0 => 0, @@ -1681,7 +1687,7 @@ Score (1,1) -> (1,16) { }, ], closeParenthesis: RParen (1,13) -> (1,14), - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -1699,29 +1705,29 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload1: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-ident 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - beats: Array [ + beats: [ Beat (1,1) -> (1,23) { notes: NoteList (1,1) -> (1,23) { - notes: Array [ + notes: [ Note (1,1) -> (1,23) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,23) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,21) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,21) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -1739,7 +1745,7 @@ Score (1,1) -> (1,23) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -1758,29 +1764,29 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload2-string 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - beats: Array [ + beats: [ Beat (1,1) -> (1,25) { notes: NoteList (1,1) -> (1,25) { - notes: Array [ + notes: [ Note (1,1) -> (1,25) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,25) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,23) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,23) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -1798,7 +1804,7 @@ Score (1,1) -> (1,25) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -1817,29 +1823,29 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload2-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-ident 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { notes: NoteList (1,1) -> (1,24) { - notes: Array [ + notes: [ Note (1,1) -> (1,24) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,24) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,22) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,22) { - arguments: Array [ + arguments: [ Ident "gradual" (1,9) -> (1,16) { parameterIndices: Map { 2 => 0, @@ -1856,7 +1862,7 @@ Score (1,1) -> (1,24) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -1875,29 +1881,29 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload3-string 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - beats: Array [ + beats: [ Beat (1,1) -> (1,26) { notes: NoteList (1,1) -> (1,26) { - notes: Array [ + notes: [ Note (1,1) -> (1,26) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,26) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,24) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,24) { - arguments: Array [ + arguments: [ String "gradual" (1,9) -> (1,17) { parameterIndices: Map { 2 => 0, @@ -1914,7 +1920,7 @@ Score (1,1) -> (1,26) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -1933,29 +1939,29 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload3-string: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-ident > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-ident 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-ident 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - beats: Array [ + beats: [ Beat (1,1) -> (1,31) { notes: NoteList (1,1) -> (1,31) { - notes: Array [ + notes: [ Note (1,1) -> (1,31) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,31) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,29) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,29) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -1978,7 +1984,7 @@ Score (1,1) -> (1,31) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -1997,29 +2003,29 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-ident: semantic-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-string > semantic-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-string 1`] = ` +exports[`AlphaTexParameterTests > handler-validation > props > vararg overloads > parenthesis > overload4-string 1`] = ` Score (1,1) -> (1,35) { - bars: Array [ + bars: [ Bar (1,1) -> (1,35) { - beats: Array [ + beats: [ Beat (1,1) -> (1,35) { notes: NoteList (1,1) -> (1,35) { - notes: Array [ + notes: [ Note (1,1) -> (1,35) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,35) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,33) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,33) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -2042,7 +2048,7 @@ Score (1,1) -> (1,35) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -2061,17 +2067,15 @@ Score (1,1) -> (1,35) { } `; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests handler-validation props vararg overloads parenthesis overload4-string: semantic-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParameterTests parser metadata empty signature empty1 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty1 1`] = ` Score (1,1) -> (1,7) { - bars: Array [ + bars: [ Bar (1,1) -> (1,7) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,4) { tag: Tag "ac" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), @@ -2079,10 +2083,10 @@ Score (1,1) -> (1,7) { }, }, ], - beats: Array [ + beats: [ Beat (1,5) -> (1,7) { notes: NoteList (1,5) -> (1,7) { - notes: Array [ + notes: [ Note (1,5) -> (1,7) { noteValue: Ident "C4" (1,5) -> (1,7), }, @@ -2095,15 +2099,15 @@ Score (1,1) -> (1,7) { } `; -exports[`AlphaTexParameterTests parser metadata empty signature empty1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata empty signature empty1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata empty signature empty2 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > empty signature > empty2 1`] = ` Score (1,1) -> (1,11) { - bars: Array [ + bars: [ Bar (1,1) -> (1,11) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,4) { tag: Tag "ac" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), @@ -2117,10 +2121,10 @@ Score (1,1) -> (1,11) { }, }, ], - beats: Array [ + beats: [ Beat (1,9) -> (1,11) { notes: NoteList (1,9) -> (1,11) { - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C4" (1,9) -> (1,11), }, @@ -2133,22 +2137,22 @@ Score (1,1) -> (1,11) { } `; -exports[`AlphaTexParameterTests parser metadata empty signature empty2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > multi > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata empty signature empty2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > multi > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list multi 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > list > multi 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,21) { tag: Tag "systemslayout" (1,1) -> (1,15) { prefix: Backslash (1,1) -> (1,2), tag: Ident "systemslayout" (1,2) -> (1,15), }, arguments: Arguments (1,16) -> (1,21) { - arguments: Array [ + arguments: [ Number "3" (1,16) -> (1,17) { parameterIndices: Map { 0 => 0, @@ -2165,7 +2169,7 @@ Score (1,1) -> (1,21) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2177,29 +2181,29 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParameterTests parser metadata list multi: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > single > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list multi: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > single > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list single 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > list > single 1`] = ` Score (1,1) -> (1,17) { - bars: Array [ + bars: [ Bar (1,1) -> (1,17) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,17) { tag: Tag "systemslayout" (1,1) -> (1,15) { prefix: Backslash (1,1) -> (1,2), tag: Ident "systemslayout" (1,2) -> (1,15), }, arguments: Arguments (1,16) -> (1,17) { - arguments: Array [ + arguments: [ Number "3" (1,16) -> (1,17) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2211,39 +2215,39 @@ Score (1,1) -> (1,17) { } `; -exports[`AlphaTexParameterTests parser metadata list single: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > switch to note > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list single: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > list > switch to note > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list switch to note 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > list > switch to note 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "ae" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), tag: Ident "ae" (1,2) -> (1,4), }, arguments: Arguments (1,5) -> (1,6) { - arguments: Array [ + arguments: [ Number "1" (1,5) -> (1,6) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,7) -> (1,10) { notes: NoteList (1,7) -> (1,10) { - notes: Array [ + notes: [ Note (1,7) -> (1,10) { noteValue: Number "1" (1,7) -> (1,8), noteStringDot: Dot (1,8) -> (1,9), @@ -2258,22 +2262,22 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser metadata list switch to note: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > multi > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata list switch to note: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > multi > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads multi 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > multi 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,25) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,25) { - arguments: Array [ + arguments: [ String "Name" (1,8) -> (1,13) { parameterIndices: Map { 0 => 0, @@ -2285,7 +2289,7 @@ Score (1,1) -> (1,25) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2297,15 +2301,15 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests parser metadata optional overloads multi: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > none > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads multi: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > none > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads none 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > none 1`] = ` Score (1,1) -> (1,7) { - bars: Array [ + bars: [ Bar (1,1) -> (1,7) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,7) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -2318,29 +2322,29 @@ Score (1,1) -> (1,7) { } `; -exports[`AlphaTexParameterTests parser metadata optional overloads none: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > single > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads none: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > single > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads single 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > optional overloads > single 1`] = ` Score (1,1) -> (1,13) { - bars: Array [ + bars: [ Bar (1,1) -> (1,13) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,13) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,13) { - arguments: Array [ + arguments: [ String "Name" (1,8) -> (1,13) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2352,22 +2356,22 @@ Score (1,1) -> (1,13) { } `; -exports[`AlphaTexParameterTests parser metadata optional overloads single: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata optional overloads single: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata required overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload1 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,15) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), tag: Ident "section" (1,2) -> (1,9), }, arguments: Arguments (1,10) -> (1,15) { - arguments: Array [ + arguments: [ String "Text" (1,10) -> (1,15) { parameterIndices: Map { 0 => 0, @@ -2375,7 +2379,7 @@ Score (1,1) -> (1,15) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -2388,22 +2392,22 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParameterTests parser metadata required overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata required overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata required overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > required overloads > overload2 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,19) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), tag: Ident "section" (1,2) -> (1,9), }, arguments: Arguments (1,10) -> (1,19) { - arguments: Array [ + arguments: [ String "T" (1,10) -> (1,12) { parameterIndices: Map { 0 => 0, @@ -2416,7 +2420,7 @@ Score (1,1) -> (1,19) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -2428,39 +2432,39 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParameterTests parser metadata required overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > correct > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata required overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > correct > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata single overload correct 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > single overload > correct 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,12) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), tag: Ident "jump" (1,2) -> (1,6), }, arguments: Arguments (1,7) -> (1,12) { - arguments: Array [ + arguments: [ Ident "Segno" (1,7) -> (1,12) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,13) -> (1,15) { notes: NoteList (1,13) -> (1,15) { - notes: Array [ + notes: [ Note (1,13) -> (1,15) { noteValue: Ident "C4" (1,13) -> (1,15), }, @@ -2473,15 +2477,29 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParameterTests parser metadata single overload correct: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata single overload correct: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > missing > parser-diagnostics 1`] = ` +[ + Map { + "code" => 219, + "severity" => 2, + "message" => "Error parsing arguments: no overload matched arguments (). Signatures: +(Ident|String)", + "start" => Map { + "col" => 6, + "line" => 1, + "offset" => 5, + }, + }, +] +`; -exports[`AlphaTexParameterTests parser metadata single overload missing 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > single overload > missing 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -2500,10 +2518,10 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser metadata single overload missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata single overload missing: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong identifier > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -2518,11 +2536,11 @@ Array [ ] `; -exports[`AlphaTexParameterTests parser metadata single overload wrong identifier 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong identifier 1`] = ` Score (1,1) -> (1,9) { - bars: Array [ + bars: [ Bar (1,1) -> (1,9) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -2530,10 +2548,10 @@ Score (1,1) -> (1,9) { }, }, ], - beats: Array [ + beats: [ Beat (1,7) -> (1,9) { notes: NoteList (1,7) -> (1,9) { - notes: Array [ + notes: [ Note (1,7) -> (1,9) { noteValue: Ident "C4" (1,7) -> (1,9), }, @@ -2546,10 +2564,10 @@ Score (1,1) -> (1,9) { } `; -exports[`AlphaTexParameterTests parser metadata single overload wrong identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata single overload wrong identifier: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong type > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -2564,11 +2582,11 @@ Array [ ] `; -exports[`AlphaTexParameterTests parser metadata single overload wrong type 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > single overload > wrong type 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -2576,10 +2594,10 @@ Score (1,1) -> (1,10) { }, }, ], - beats: Array [ + beats: [ Beat (1,7) -> (1,10) { notes: NoteList (1,7) -> (1,10) { - notes: Array [ + notes: [ Note (1,7) -> (1,10) { noteValue: Number "3" (1,7) -> (1,8), noteStringDot: Dot (1,8) -> (1,9), @@ -2594,36 +2612,22 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser metadata single overload wrong type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata single overload wrong type: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (). Signatures: -(Ident|String)", - "start" => Map { - "col" => 6, - "line" => 1, - "offset" => 5, - }, - }, -] -`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload1 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), tag: Ident "articulation" (1,2) -> (1,14), }, arguments: Arguments (1,15) -> (1,23) { - arguments: Array [ + arguments: [ Ident "defaults" (1,15) -> (1,23) { parameterIndices: Map { 0 => 0, @@ -2631,7 +2635,7 @@ Score (1,1) -> (1,23) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -2644,22 +2648,22 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests parser metadata type overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload2 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,20) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), tag: Ident "articulation" (1,2) -> (1,14), }, arguments: Arguments (1,15) -> (1,20) { - arguments: Array [ + arguments: [ String "A" (1,15) -> (1,17) { parameterIndices: Map { 1 => 0, @@ -2671,7 +2675,7 @@ Score (1,1) -> (1,20) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -2683,22 +2687,22 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParameterTests parser metadata type overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload3 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload3 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload3 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > type overloads > overload3 1`] = ` Score (1,1) -> (1,18) { - bars: Array [ + bars: [ Bar (1,1) -> (1,18) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,18) { tag: Tag "articulation" (1,1) -> (1,14) { prefix: Backslash (1,1) -> (1,2), tag: Ident "articulation" (1,2) -> (1,14), }, arguments: Arguments (1,15) -> (1,18) { - arguments: Array [ + arguments: [ Ident "A" (1,15) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -2710,7 +2714,7 @@ Score (1,1) -> (1,18) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -2722,29 +2726,29 @@ Score (1,1) -> (1,18) { } `; -exports[`AlphaTexParameterTests parser metadata type overloads overload3: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata type overloads overload3: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata value overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload1 1`] = ` Score (1,1) -> (1,11) { - bars: Array [ + bars: [ Bar (1,1) -> (1,11) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,11) { tag: Tag "ts" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), tag: Ident "ts" (1,2) -> (1,4), }, arguments: Arguments (1,5) -> (1,11) { - arguments: Array [ + arguments: [ Ident "common" (1,5) -> (1,11) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2756,22 +2760,22 @@ Score (1,1) -> (1,11) { } `; -exports[`AlphaTexParameterTests parser metadata value overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata value overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata value overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > parser > metadata > value overloads > overload2 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,8) { tag: Tag "ts" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), tag: Ident "ts" (1,2) -> (1,4), }, arguments: Arguments (1,5) -> (1,8) { - arguments: Array [ + arguments: [ Number "3" (1,5) -> (1,6) { parameterIndices: Map { 1 => 0, @@ -2783,7 +2787,7 @@ Score (1,1) -> (1,8) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -2795,27 +2799,27 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParameterTests parser metadata value overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > ambiguous > incomplete > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser metadata value overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > ambiguous > incomplete > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props ambiguous incomplete 1`] = ` +exports[`AlphaTexParameterTests > parser > props > ambiguous > incomplete 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,12) { - notes: Array [ + notes: [ Note (1,1) -> (1,12) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,12) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,10) { property: Ident "tu" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,10) { - arguments: Array [ + arguments: [ Number "3" (1,9) -> (1,10) { parameterIndices: Map { 0 => 0, @@ -2823,7 +2827,7 @@ Score (1,1) -> (1,12) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -2843,23 +2847,23 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParameterTests parser props ambiguous incomplete: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > empty signature > empty1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props ambiguous incomplete: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > empty signature > empty1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props empty signature empty1 1`] = ` +exports[`AlphaTexParameterTests > parser > props > empty signature > empty1 1`] = ` Score (1,1) -> (1,7) { - bars: Array [ + bars: [ Bar (1,1) -> (1,7) { - beats: Array [ + beats: [ Beat (1,1) -> (1,7) { notes: NoteList (1,1) -> (1,7) { - notes: Array [ + notes: [ Note (1,1) -> (1,7) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,7) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,6) { property: Ident "v" (1,5) -> (1,6), }, @@ -2876,23 +2880,23 @@ Score (1,1) -> (1,7) { } `; -exports[`AlphaTexParameterTests parser props empty signature empty1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > empty signature > empty2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props empty signature empty1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > empty signature > empty2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props empty signature empty2 1`] = ` +exports[`AlphaTexParameterTests > parser > props > empty signature > empty2 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,10) { - notes: Array [ + notes: [ Note (1,1) -> (1,10) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,10) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,6) { property: Ident "v" (1,5) -> (1,6), }, @@ -2912,22 +2916,22 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser props empty signature empty2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props empty signature empty2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props mixed overloads overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload1 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,15) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), tag: Ident "section" (1,2) -> (1,9), }, arguments: Arguments (1,10) -> (1,15) { - arguments: Array [ + arguments: [ String "Text" (1,10) -> (1,15) { parameterIndices: Map { 0 => 0, @@ -2935,7 +2939,7 @@ Score (1,1) -> (1,15) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -2948,22 +2952,22 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParameterTests parser props mixed overloads overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props mixed overloads overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props mixed overloads overload2 1`] = ` +exports[`AlphaTexParameterTests > parser > props > mixed overloads > overload2 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,19) { tag: Tag "section" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), tag: Ident "section" (1,2) -> (1,9), }, arguments: Arguments (1,10) -> (1,19) { - arguments: Array [ + arguments: [ String "T" (1,10) -> (1,12) { parameterIndices: Map { 0 => 0, @@ -2976,7 +2980,7 @@ Score (1,1) -> (1,19) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -2988,34 +2992,34 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParameterTests parser props mixed overloads overload2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > correct > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props mixed overloads overload2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > correct > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload correct 1`] = ` +exports[`AlphaTexParameterTests > parser > props > optional overload > correct 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,12) { - notes: Array [ + notes: [ Note (1,1) -> (1,12) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,12) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,11) { property: Ident "ad" (1,5) -> (1,7), properties: Arguments (1,8) -> (1,11) { - arguments: Array [ + arguments: [ Number "100" (1,8) -> (1,11) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -3034,23 +3038,23 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParameterTests parser props optional overload correct: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > missing1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload correct: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > missing1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload missing1 1`] = ` +exports[`AlphaTexParameterTests > parser > props > optional overload > missing1 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,8) { - notes: Array [ + notes: [ Note (1,1) -> (1,8) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,8) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,7) { property: Ident "ad" (1,5) -> (1,7), }, @@ -3067,23 +3071,23 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParameterTests parser props optional overload missing1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > missing2 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload missing1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > missing2 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload missing2 1`] = ` +exports[`AlphaTexParameterTests > parser > props > optional overload > missing2 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,10) { - notes: Array [ + notes: [ Note (1,1) -> (1,10) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,10) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,7) { property: Ident "ad" (1,5) -> (1,7), }, @@ -3103,23 +3107,47 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser props optional overload missing2: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload missing2: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong identifier > parser-diagnostics 1`] = ` +[ + Map { + "code" => 219, + "severity" => 2, + "message" => "Error parsing arguments: no overload matched arguments (). Signatures: +(Ident|String)", + "start" => Map { + "col" => 9, + "line" => 1, + "offset" => 8, + }, + }, + Map { + "code" => 205, + "severity" => 2, + "message" => "Unrecognized property 'invalid'.", + "start" => Map { + "col" => 17, + "line" => 1, + "offset" => 16, + }, + }, +] +`; -exports[`AlphaTexParameterTests parser props optional overload wrong identifier 1`] = ` +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong identifier 1`] = ` Score (1,1) -> (1,18) { - bars: Array [ + bars: [ Bar (1,1) -> (1,18) { - beats: Array [ + beats: [ Beat (1,1) -> (1,18) { notes: NoteList (1,1) -> (1,18) { - notes: Array [ + notes: [ Note (1,1) -> (1,18) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,18) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,9) { property: Ident "beam" (1,5) -> (1,9), }, @@ -3139,47 +3167,37 @@ Score (1,1) -> (1,18) { } `; -exports[`AlphaTexParameterTests parser props optional overload wrong identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload wrong identifier: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (). Signatures: -(Ident|String)", - "start" => Map { - "col" => 9, - "line" => 1, - "offset" => 8, - }, - }, +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong type > parser-diagnostics 1`] = ` +[ Map { - "code" => 205, + "code" => 220, "severity" => 2, - "message" => "Unrecognized property 'invalid'.", + "message" => "Error parsing arguments: unexpected additional arguments. Signatures: +(Number)", "start" => Map { - "col" => 17, + "col" => 10, "line" => 1, - "offset" => 16, + "offset" => 9, }, }, ] `; -exports[`AlphaTexParameterTests parser props optional overload wrong type 1`] = ` +exports[`AlphaTexParameterTests > parser > props > optional overload > wrong type 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,12) { - notes: Array [ + notes: [ Note (1,1) -> (1,12) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,12) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,10) { property: Ident "ad" (1,5) -> (1,7), }, @@ -3196,53 +3214,39 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParameterTests parser props optional overload wrong type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > single overload > correct > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props optional overload wrong type: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 220, - "severity" => 2, - "message" => "Error parsing arguments: unexpected additional arguments. Signatures: -(Number)", - "start" => Map { - "col" => 10, - "line" => 1, - "offset" => 9, - }, - }, -] -`; +exports[`AlphaTexParameterTests > parser > props > single overload > correct > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props single overload correct 1`] = ` +exports[`AlphaTexParameterTests > parser > props > single overload > correct 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,12) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), tag: Ident "jump" (1,2) -> (1,6), }, arguments: Arguments (1,7) -> (1,12) { - arguments: Array [ + arguments: [ Ident "Segno" (1,7) -> (1,12) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,13) -> (1,15) { notes: NoteList (1,13) -> (1,15) { - notes: Array [ + notes: [ Note (1,13) -> (1,15) { noteValue: Ident "C4" (1,13) -> (1,15), }, @@ -3255,15 +3259,29 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParameterTests parser props single overload correct: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > single overload > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props single overload correct: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > single overload > missing > parser-diagnostics 1`] = ` +[ + Map { + "code" => 219, + "severity" => 2, + "message" => "Error parsing arguments: no overload matched arguments (). Signatures: +(Ident|String)", + "start" => Map { + "col" => 6, + "line" => 1, + "offset" => 5, + }, + }, +] +`; -exports[`AlphaTexParameterTests parser props single overload missing 1`] = ` +exports[`AlphaTexParameterTests > parser > props > single overload > missing 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -3282,10 +3300,10 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser props single overload missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > single overload > wrong identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props single overload missing: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > parser > props > single overload > wrong identifier > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -3300,11 +3318,11 @@ Array [ ] `; -exports[`AlphaTexParameterTests parser props single overload wrong identifier 1`] = ` +exports[`AlphaTexParameterTests > parser > props > single overload > wrong identifier 1`] = ` Score (1,1) -> (1,9) { - bars: Array [ + bars: [ Bar (1,1) -> (1,9) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -3312,10 +3330,10 @@ Score (1,1) -> (1,9) { }, }, ], - beats: Array [ + beats: [ Beat (1,7) -> (1,9) { notes: NoteList (1,7) -> (1,9) { - notes: Array [ + notes: [ Note (1,7) -> (1,9) { noteValue: Ident "C4" (1,7) -> (1,9), }, @@ -3328,10 +3346,10 @@ Score (1,1) -> (1,9) { } `; -exports[`AlphaTexParameterTests parser props single overload wrong identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > single overload > wrong type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props single overload wrong identifier: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParameterTests > parser > props > single overload > wrong type > parser-diagnostics 1`] = ` +[ Map { "code" => 219, "severity" => 2, @@ -3346,11 +3364,11 @@ Array [ ] `; -exports[`AlphaTexParameterTests parser props single overload wrong type 1`] = ` +exports[`AlphaTexParameterTests > parser > props > single overload > wrong type 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,6) { tag: Tag "jump" (1,1) -> (1,6) { prefix: Backslash (1,1) -> (1,2), @@ -3358,10 +3376,10 @@ Score (1,1) -> (1,10) { }, }, ], - beats: Array [ + beats: [ Beat (1,7) -> (1,10) { notes: NoteList (1,7) -> (1,10) { - notes: Array [ + notes: [ Note (1,7) -> (1,10) { noteValue: Number "3" (1,7) -> (1,8), noteStringDot: Dot (1,8) -> (1,9), @@ -3376,29 +3394,15 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParameterTests parser props single overload wrong type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload1-number > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props single overload wrong type: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (). Signatures: -(Ident|String)", - "start" => Map { - "col" => 6, - "line" => 1, - "offset" => 5, - }, - }, -] -`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload1-number > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload1-number 1`] = ` +exports[`AlphaTexParameterTests > parser > props > type overloads > overload1-number 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,25) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -3406,18 +3410,18 @@ Score (1,1) -> (1,25) { }, properties: Props (1,8) -> (1,25) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,23) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,23) { - arguments: Array [ + arguments: [ Number "20" (1,21) -> (1,23) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -3433,15 +3437,15 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests parser props type overloads overload1-number: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-identifier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload1-number: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-identifier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload2-identifier 1`] = ` +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-identifier 1`] = ` Score (1,1) -> (1,41) { - bars: Array [ + bars: [ Bar (1,1) -> (1,41) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,41) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -3449,18 +3453,18 @@ Score (1,1) -> (1,41) { }, properties: Props (1,8) -> (1,41) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,39) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,39) { - arguments: Array [ + arguments: [ Ident "AcousticGrandPiano" (1,21) -> (1,39) { parameterIndices: Map { 1 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3476,15 +3480,15 @@ Score (1,1) -> (1,41) { } `; -exports[`AlphaTexParameterTests parser props type overloads overload2-identifier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload2-identifier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload2-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > type overloads > overload2-string 1`] = ` Score (1,1) -> (1,44) { - bars: Array [ + bars: [ Bar (1,1) -> (1,44) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,44) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -3492,18 +3496,18 @@ Score (1,1) -> (1,44) { }, properties: Props (1,8) -> (1,44) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,42) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,42) { - arguments: Array [ + arguments: [ String "Acoustic Grand Piano" (1,21) -> (1,42) { parameterIndices: Map { 1 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3519,15 +3523,15 @@ Score (1,1) -> (1,44) { } `; -exports[`AlphaTexParameterTests parser props type overloads overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload3-percussion > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > type overloads > overload3-percussion > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload3-percussion 1`] = ` +exports[`AlphaTexParameterTests > parser > props > type overloads > overload3-percussion 1`] = ` Score (1,1) -> (1,33) { - bars: Array [ + bars: [ Bar (1,1) -> (1,33) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,33) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -3535,11 +3539,11 @@ Score (1,1) -> (1,33) { }, properties: Props (1,8) -> (1,33) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,31) { property: Ident "instrument" (1,10) -> (1,20), properties: Arguments (1,21) -> (1,31) { - arguments: Array [ + arguments: [ Ident "percussion" (1,21) -> (1,31) { parameterIndices: Map { 1 => 0, @@ -3547,7 +3551,7 @@ Score (1,1) -> (1,33) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, 1, ], @@ -3564,27 +3568,27 @@ Score (1,1) -> (1,33) { } `; -exports[`AlphaTexParameterTests parser props type overloads overload3-percussion: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props type overloads overload3-percussion: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload1 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,14) { - notes: Array [ + notes: [ Note (1,1) -> (1,14) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,14) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,12) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,12) { - arguments: Array [ + arguments: [ Number "0" (1,9) -> (1,10) { parameterIndices: Map { 0 => 0, @@ -3596,7 +3600,7 @@ Score (1,1) -> (1,14) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -3615,27 +3619,27 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-ident 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - beats: Array [ + beats: [ Beat (1,1) -> (1,21) { notes: NoteList (1,1) -> (1,21) { - notes: Array [ + notes: [ Note (1,1) -> (1,21) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,21) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,19) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,19) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -3653,7 +3657,7 @@ Score (1,1) -> (1,21) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3672,27 +3676,27 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload2-string 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - beats: Array [ + beats: [ Beat (1,1) -> (1,23) { notes: NoteList (1,1) -> (1,23) { - notes: Array [ + notes: [ Note (1,1) -> (1,23) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,23) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,21) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,21) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -3710,7 +3714,7 @@ Score (1,1) -> (1,23) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3729,27 +3733,27 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-ident 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,1) -> (1,22) { - beats: Array [ + beats: [ Beat (1,1) -> (1,22) { notes: NoteList (1,1) -> (1,22) { - notes: Array [ + notes: [ Note (1,1) -> (1,22) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,22) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,20) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,20) { - arguments: Array [ + arguments: [ Ident "gradual" (1,9) -> (1,16) { parameterIndices: Map { 2 => 0, @@ -3766,7 +3770,7 @@ Score (1,1) -> (1,22) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -3785,27 +3789,27 @@ Score (1,1) -> (1,22) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload3-string 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { notes: NoteList (1,1) -> (1,24) { - notes: Array [ + notes: [ Note (1,1) -> (1,24) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,24) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,22) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,22) { - arguments: Array [ + arguments: [ String "gradual" (1,9) -> (1,17) { parameterIndices: Map { 2 => 0, @@ -3822,7 +3826,7 @@ Score (1,1) -> (1,24) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -3841,27 +3845,27 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload3-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-ident 1`] = ` Score (1,1) -> (1,29) { - bars: Array [ + bars: [ Bar (1,1) -> (1,29) { - beats: Array [ + beats: [ Beat (1,1) -> (1,29) { notes: NoteList (1,1) -> (1,29) { - notes: Array [ + notes: [ Note (1,1) -> (1,29) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,29) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,27) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,27) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -3884,7 +3888,7 @@ Score (1,1) -> (1,29) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -3903,27 +3907,27 @@ Score (1,1) -> (1,29) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > flat > overload4-string 1`] = ` Score (1,1) -> (1,33) { - bars: Array [ + bars: [ Bar (1,1) -> (1,33) { - beats: Array [ + beats: [ Beat (1,1) -> (1,33) { notes: NoteList (1,1) -> (1,33) { - notes: Array [ + notes: [ Note (1,1) -> (1,33) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,33) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,31) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,31) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -3946,7 +3950,7 @@ Score (1,1) -> (1,33) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -3965,28 +3969,28 @@ Score (1,1) -> (1,33) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload1 > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads flat overload4-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload1 > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload1 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload1 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,16) { notes: NoteList (1,1) -> (1,16) { - notes: Array [ + notes: [ Note (1,1) -> (1,16) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,16) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,14) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,14) { openParenthesis: LParen (1,9) -> (1,10), - arguments: Array [ + arguments: [ Number "0" (1,10) -> (1,11), Number "4" (1,12) -> (1,13), ], @@ -4006,27 +4010,27 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload1: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload1: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-ident 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - beats: Array [ + beats: [ Beat (1,1) -> (1,23) { notes: NoteList (1,1) -> (1,23) { - notes: Array [ + notes: [ Note (1,1) -> (1,23) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,23) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,21) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,21) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -4044,7 +4048,7 @@ Score (1,1) -> (1,23) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4063,27 +4067,27 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload2-string 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - beats: Array [ + beats: [ Beat (1,1) -> (1,25) { notes: NoteList (1,1) -> (1,25) { - notes: Array [ + notes: [ Note (1,1) -> (1,25) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,25) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,23) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,23) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -4101,7 +4105,7 @@ Score (1,1) -> (1,25) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4120,27 +4124,27 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload2-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-ident 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { notes: NoteList (1,1) -> (1,24) { - notes: Array [ + notes: [ Note (1,1) -> (1,24) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,24) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,22) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,22) { - arguments: Array [ + arguments: [ Ident "gradual" (1,9) -> (1,16) { parameterIndices: Map { 2 => 0, @@ -4157,7 +4161,7 @@ Score (1,1) -> (1,24) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -4176,27 +4180,27 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload3-string 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - beats: Array [ + beats: [ Beat (1,1) -> (1,26) { notes: NoteList (1,1) -> (1,26) { - notes: Array [ + notes: [ Note (1,1) -> (1,26) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,26) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,24) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,24) { - arguments: Array [ + arguments: [ String "gradual" (1,9) -> (1,17) { parameterIndices: Map { 2 => 0, @@ -4213,7 +4217,7 @@ Score (1,1) -> (1,26) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 2, ], validated: true, @@ -4232,27 +4236,27 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-ident > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload3-string: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-ident > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-ident 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-ident 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - beats: Array [ + beats: [ Beat (1,1) -> (1,31) { notes: NoteList (1,1) -> (1,31) { - notes: Array [ + notes: [ Note (1,1) -> (1,31) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,31) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,29) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,29) { - arguments: Array [ + arguments: [ Ident "custom" (1,9) -> (1,15) { parameterIndices: Map { 1 => 0, @@ -4275,7 +4279,7 @@ Score (1,1) -> (1,31) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -4294,27 +4298,27 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-ident: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-ident: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-string > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-string 1`] = ` +exports[`AlphaTexParameterTests > parser > props > vararg overloads > parenthesis > overload4-string 1`] = ` Score (1,1) -> (1,35) { - bars: Array [ + bars: [ Bar (1,1) -> (1,35) { - beats: Array [ + beats: [ Beat (1,1) -> (1,35) { notes: NoteList (1,1) -> (1,35) { - notes: Array [ + notes: [ Note (1,1) -> (1,35) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,35) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,33) { property: Ident "tb" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,33) { - arguments: Array [ + arguments: [ String "custom" (1,9) -> (1,16) { parameterIndices: Map { 1 => 0, @@ -4337,7 +4341,7 @@ Score (1,1) -> (1,35) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 3, ], validated: true, @@ -4355,7 +4359,3 @@ Score (1,1) -> (1,35) { ], } `; - -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-string: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParameterTests parser props vararg overloads parenthesis overload4-string: parser-diagnostics 1`] = `Array []`; diff --git a/packages/alphatab/test/importer/__snapshots__/AlphaTexParser.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/AlphaTexParser.test.ts.snap index 101ce7eb2..cc17a13f8 100644 --- a/packages/alphatab/test/importer/__snapshots__/AlphaTexParser.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/AlphaTexParser.test.ts.snap @@ -1,17 +1,21 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AlphaTexParserTest ambiguous tempo and stringed note 1`] = ` +exports[`AlphaTexParserTest > ambiguous > tempo and stringed note > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > ambiguous > tempo and stringed note > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > ambiguous > tempo and stringed note 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,11) { tag: Tag "tempo" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "tempo" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,11) { - arguments: Array [ + arguments: [ Number "120" (1,8) -> (1,11) { parameterIndices: Map { 0 => 0, @@ -19,7 +23,7 @@ Score (1,1) -> (1,19) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, 1, ], @@ -27,10 +31,10 @@ Score (1,1) -> (1,19) { }, }, ], - beats: Array [ + beats: [ Beat (1,12) -> (1,15) { notes: NoteList (1,12) -> (1,15) { - notes: Array [ + notes: [ Note (1,12) -> (1,15) { noteValue: Number "3" (1,12) -> (1,13), noteStringDot: Dot (1,13) -> (1,14), @@ -41,7 +45,7 @@ Score (1,1) -> (1,19) { }, Beat (1,16) -> (1,19) { notes: NoteList (1,16) -> (1,19) { - notes: Array [ + notes: [ Note (1,16) -> (1,19) { noteValue: Number "3" (1,16) -> (1,17), noteStringDot: Dot (1,17) -> (1,18), @@ -56,19 +60,15 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest ambiguous tempo and stringed note: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest ambiguous tempo and stringed note: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest ambiguous tempo, temponame and stringed note: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > ambiguous > voice followed by note list > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest ambiguous tempo, temponame and stringed note: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > ambiguous > voice followed by note list > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest ambiguous voice followed by note list 1`] = ` +exports[`AlphaTexParserTest > ambiguous > voice followed by note list 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,7) { tag: Tag "voice" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -76,11 +76,11 @@ Score (1,1) -> (1,15) { }, }, ], - beats: Array [ + beats: [ Beat (1,8) -> (1,15) { notes: NoteList (1,8) -> (1,15) { openParenthesis: LParen (1,8) -> (1,9), - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C4" (1,9) -> (1,11), }, @@ -97,25 +97,25 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParserTest ambiguous voice followed by note list: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > bar meta singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest ambiguous voice followed by note list: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > bar meta singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments bar meta singleline 1`] = ` +exports[`AlphaTexParserTest > comments > bar meta singleline 1`] = ` Score (1,1) -> (3,9) { - bars: Array [ + bars: [ Bar (1,1) -> (3,9) { - metaData: Array [ + metaData: [ Meta (3,2) -> (3,9) { tag: Tag "ts" (3,2) -> (3,5) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], prefix: Backslash (3,2) -> (3,3), tag: Ident "ts" (3,3) -> (3,5), }, arguments: Arguments (3,6) -> (3,9) { - arguments: Array [ + arguments: [ Number "3" (3,6) -> (3,7) { parameterIndices: Map { 1 => 0, @@ -127,7 +127,7 @@ Score (1,1) -> (3,9) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -139,18 +139,18 @@ Score (1,1) -> (3,9) { } `; -exports[`AlphaTexParserTest comments bar meta singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > bar multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments bar meta singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > bar multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments bar multiline 1`] = ` +exports[`AlphaTexParserTest > comments > bar multiline 1`] = ` Score (1,1) -> (3,15) { - bars: Array [ + bars: [ Bar (1,1) -> (3,15) { - metaData: Array [ + metaData: [ Meta (3,8) -> (3,15) { tag: Tag "ts" (3,8) -> (3,11) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -158,7 +158,7 @@ line**/", tag: Ident "ts" (3,9) -> (3,11), }, arguments: Arguments (3,12) -> (3,15) { - arguments: Array [ + arguments: [ Number "3" (3,12) -> (3,13) { parameterIndices: Map { 1 => 0, @@ -170,7 +170,7 @@ line**/", }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -182,20 +182,24 @@ line**/", } `; -exports[`AlphaTexParserTest comments bar multiline middle 1`] = ` +exports[`AlphaTexParserTest > comments > bar multiline middle > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > comments > bar multiline middle > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > comments > bar multiline middle 1`] = ` Score (1,1) -> (3,9) { - bars: Array [ + bars: [ Bar (1,1) -> (3,9) { - metaData: Array [ + metaData: [ Meta (2,1) -> (3,9) { tag: Tag "ts" (2,1) -> (2,4) { prefix: Backslash (2,1) -> (2,2), tag: Ident "ts" (2,2) -> (2,4), }, arguments: Arguments (2,5) -> (3,9) { - arguments: Array [ + arguments: [ Number "3" (2,5) -> (2,6) { - trailingComments: Array [ + trailingComments: [ "/** multi line**/", ], @@ -209,7 +213,7 @@ line**/", }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -221,29 +225,25 @@ line**/", } `; -exports[`AlphaTexParserTest comments bar multiline middle: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest comments bar multiline middle: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest comments bar multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat chord multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments bar multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat chord multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat chord multiline 1`] = ` +exports[`AlphaTexParserTest > comments > beat chord multiline 1`] = ` Score (1,1) -> (3,11) { - bars: Array [ + bars: [ Bar (1,1) -> (3,11) { - beats: Array [ + beats: [ Beat (1,3) -> (3,11) { notes: NoteList (1,3) -> (3,11) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, Note (3,8) -> (3,10) { noteValue: Ident "C5" (3,8) -> (3,10) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -259,25 +259,25 @@ line**/", } `; -exports[`AlphaTexParserTest comments beat chord multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat chord singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat chord multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat chord singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat chord singleline 1`] = ` +exports[`AlphaTexParserTest > comments > beat chord singleline 1`] = ` Score (1,1) -> (3,5) { - bars: Array [ + bars: [ Bar (1,1) -> (3,5) { - beats: Array [ + beats: [ Beat (1,3) -> (3,5) { notes: NoteList (1,3) -> (3,5) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, Note (3,2) -> (3,4) { noteValue: Ident "C5" (3,2) -> (3,4) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], }, @@ -292,18 +292,18 @@ Score (1,1) -> (3,5) { } `; -exports[`AlphaTexParserTest comments beat chord singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat chord singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat multiline 1`] = ` +exports[`AlphaTexParserTest > comments > beat multiline 1`] = ` Score (1,1) -> (3,10) { - bars: Array [ + bars: [ Bar (1,1) -> (3,10) { - beats: Array [ + beats: [ Beat (1,3) -> (1,5) { notes: NoteList (1,3) -> (1,5) { - notes: Array [ + notes: [ Note (1,3) -> (1,5) { noteValue: Ident "C4" (1,3) -> (1,5), }, @@ -312,10 +312,10 @@ Score (1,1) -> (3,10) { }, Beat (3,8) -> (3,10) { notes: NoteList (3,8) -> (3,10) { - notes: Array [ + notes: [ Note (3,8) -> (3,10) { noteValue: Ident "C5" (3,8) -> (3,10) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -330,18 +330,18 @@ line**/", } `; -exports[`AlphaTexParserTest comments beat multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beat singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat singleline 1`] = ` +exports[`AlphaTexParserTest > comments > beat singleline 1`] = ` Score (1,1) -> (3,4) { - bars: Array [ + bars: [ Bar (1,1) -> (3,4) { - beats: Array [ + beats: [ Beat (1,3) -> (1,5) { notes: NoteList (1,3) -> (1,5) { - notes: Array [ + notes: [ Note (1,3) -> (1,5) { noteValue: Ident "C4" (1,3) -> (1,5), }, @@ -350,10 +350,10 @@ Score (1,1) -> (3,4) { }, Beat (3,2) -> (3,4) { notes: NoteList (3,2) -> (3,4) { - notes: Array [ + notes: [ Note (3,2) -> (3,4) { noteValue: Ident "C5" (3,2) -> (3,4) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], }, @@ -367,19 +367,19 @@ Score (1,1) -> (3,4) { } `; -exports[`AlphaTexParserTest comments beat singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beateffects multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beat singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beateffects multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beateffects multiline 1`] = ` +exports[`AlphaTexParserTest > comments > beateffects multiline 1`] = ` Score (1,1) -> (3,9) { - bars: Array [ + bars: [ Bar (1,1) -> (3,9) { - beats: Array [ + beats: [ Beat (1,3) -> (3,9) { notes: NoteList (1,3) -> (1,10) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -391,9 +391,9 @@ Score (1,1) -> (3,9) { }, beatEffects: Props (1,11) -> (3,9) { openBrace: LBrace (1,11) -> (1,12), - properties: Array [], + properties: [], closeBrace: RBrace (3,8) -> (3,9) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -406,19 +406,19 @@ line**/", } `; -exports[`AlphaTexParserTest comments beateffects multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beateffects singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beateffects multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > beateffects singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beateffects singleline 1`] = ` +exports[`AlphaTexParserTest > comments > beateffects singleline 1`] = ` Score (1,1) -> (3,3) { - bars: Array [ + bars: [ Bar (1,1) -> (3,3) { - beats: Array [ + beats: [ Beat (1,3) -> (3,3) { notes: NoteList (1,3) -> (1,10) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -430,9 +430,9 @@ Score (1,1) -> (3,3) { }, beatEffects: Props (1,11) -> (3,3) { openBrace: LBrace (1,11) -> (1,12), - properties: Array [], + properties: [], closeBrace: RBrace (3,2) -> (3,3) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], }, @@ -444,26 +444,26 @@ Score (1,1) -> (3,3) { } `; -exports[`AlphaTexParserTest comments beateffects singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > noteeffects multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments beateffects singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > noteeffects multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments noteeffects multiline 1`] = ` +exports[`AlphaTexParserTest > comments > noteeffects multiline 1`] = ` Score (1,1) -> (3,13) { - bars: Array [ + bars: [ Bar (1,1) -> (3,13) { - beats: Array [ + beats: [ Beat (1,3) -> (3,13) { notes: NoteList (1,3) -> (3,13) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (3,9) { noteValue: Ident "C4" (1,4) -> (1,6), noteEffects: Props (1,9) -> (3,9) { openBrace: LBrace (1,9) -> (1,10), - properties: Array [], + properties: [], closeBrace: RBrace (3,8) -> (3,9) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -483,26 +483,26 @@ line**/", } `; -exports[`AlphaTexParserTest comments noteeffects multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > noteeffects singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments noteeffects multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > noteeffects singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments noteeffects singleline 1`] = ` +exports[`AlphaTexParserTest > comments > noteeffects singleline 1`] = ` Score (1,1) -> (3,7) { - bars: Array [ + bars: [ Bar (1,1) -> (3,7) { - beats: Array [ + beats: [ Beat (1,3) -> (3,7) { notes: NoteList (1,3) -> (3,7) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (3,3) { noteValue: Ident "C4" (1,4) -> (1,6), noteEffects: Props (1,7) -> (3,3) { openBrace: LBrace (1,7) -> (1,8), - properties: Array [], + properties: [], closeBrace: RBrace (3,2) -> (3,3) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], }, @@ -521,18 +521,18 @@ Score (1,1) -> (3,7) { } `; -exports[`AlphaTexParserTest comments noteeffects singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > score meta multiline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments noteeffects singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > score meta multiline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments score meta multiline 1`] = ` +exports[`AlphaTexParserTest > comments > score meta multiline 1`] = ` Score (1,1) -> (2,20) { - bars: Array [ + bars: [ Bar (2,8) -> (2,20) { - metaData: Array [ + metaData: [ Meta (2,8) -> (2,20) { tag: Tag "title" (2,8) -> (2,14) { - leadingComments: Array [ + leadingComments: [ "/** multi line**/", ], @@ -540,14 +540,14 @@ line**/", tag: Ident "title" (2,9) -> (2,14), }, arguments: Arguments (2,15) -> (2,20) { - arguments: Array [ + arguments: [ String "Test" (2,15) -> (2,20) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -559,14 +559,18 @@ line**/", } `; -exports[`AlphaTexParserTest comments score meta multiline middle 1`] = ` +exports[`AlphaTexParserTest > comments > score meta multiline middle > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > comments > score meta multiline middle > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > comments > score meta multiline middle 1`] = ` Score (1,1) -> (2,13) { - bars: Array [ + bars: [ Bar (1,1) -> (2,13) { - metaData: Array [ + metaData: [ Meta (1,1) -> (2,13) { tag: Tag "title" (1,1) -> (1,7) { - trailingComments: Array [ + trailingComments: [ "/** multi line**/", ], @@ -574,14 +578,14 @@ line**/", tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (2,8) -> (2,13) { - arguments: Array [ + arguments: [ String "Test" (2,8) -> (2,13) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -593,36 +597,32 @@ line**/", } `; -exports[`AlphaTexParserTest comments score meta multiline middle: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest comments score meta multiline middle: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest comments score meta multiline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > score meta singleline > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments score meta multiline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > comments > score meta singleline > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments score meta singleline 1`] = ` +exports[`AlphaTexParserTest > comments > score meta singleline 1`] = ` Score (1,1) -> (2,14) { - bars: Array [ + bars: [ Bar (2,2) -> (2,14) { - metaData: Array [ + metaData: [ Meta (2,2) -> (2,14) { tag: Tag "title" (2,2) -> (2,8) { - leadingComments: Array [ + leadingComments: [ "// Single ", ], prefix: Backslash (2,2) -> (2,3), tag: Ident "title" (2,3) -> (2,8), }, arguments: Arguments (2,9) -> (2,14) { - arguments: Array [ + arguments: [ String "Test" (2,9) -> (2,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -634,19 +634,32 @@ Score (1,1) -> (2,14) { } `; -exports[`AlphaTexParserTest comments score meta singleline: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at200 > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest comments score meta singleline: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at200 > missing > parser-diagnostics 1`] = ` +[ + Map { + "code" => 200, + "severity" => 2, + "message" => "Missing beat multiplier value after '*'.", + "start" => Map { + "col" => 8, + "line" => 1, + "offset" => 7, + }, + }, +] +`; -exports[`AlphaTexParserTest errors at200 missing 1`] = ` +exports[`AlphaTexParserTest > errors > at200 > missing 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,6) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -663,10 +676,10 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParserTest errors at200 missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at200 > type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at200 missing: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at200 > type > parser-diagnostics 1`] = ` +[ Map { "code" => 200, "severity" => 2, @@ -680,15 +693,15 @@ Array [ ] `; -exports[`AlphaTexParserTest errors at200 type 1`] = ` +exports[`AlphaTexParserTest > errors > at200 > type 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,6) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -701,7 +714,7 @@ Score (1,1) -> (1,10) { }, Beat (1,9) -> (1,10) { notes: NoteList (1,9) -> (1,10) { - notes: Array [ + notes: [ Note (1,9) -> (1,10) { noteValue: Ident "A" (1,9) -> (1,10), }, @@ -714,28 +727,28 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParserTest errors at200 type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at201 > missing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at200 type: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at201 > missing > parser-diagnostics 1`] = ` +[ Map { - "code" => 200, + "code" => 201, "severity" => 2, - "message" => "Missing beat multiplier value after '*'.", + "message" => "Missing duration value after ':'.", "start" => Map { - "col" => 8, + "col" => 2, "line" => 1, - "offset" => 7, + "offset" => 1, }, }, ] `; -exports[`AlphaTexParserTest errors at201 missing 1`] = ` +exports[`AlphaTexParserTest > errors > at201 > missing 1`] = ` Score (1,1) -> (1,2) { - bars: Array [ + bars: [ Bar (1,1) -> (1,2) { - beats: Array [ + beats: [ Beat (1,1) -> (1,2) { durationChange: Duration (1,1) -> (1,2) { colon: Colon (1,1) -> (1,2), @@ -747,10 +760,10 @@ Score (1,1) -> (1,2) { } `; -exports[`AlphaTexParserTest errors at201 missing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at201 > type > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at201 missing: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at201 > type > parser-diagnostics 1`] = ` +[ Map { "code" => 201, "severity" => 2, @@ -764,17 +777,17 @@ Array [ ] `; -exports[`AlphaTexParserTest errors at201 type 1`] = ` +exports[`AlphaTexParserTest > errors > at201 > type 1`] = ` Score (1,1) -> (1,3) { - bars: Array [ + bars: [ Bar (1,1) -> (1,3) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3) { durationChange: Duration (1,1) -> (1,2) { colon: Colon (1,1) -> (1,2), }, notes: NoteList (1,2) -> (1,3) { - notes: Array [ + notes: [ Note (1,2) -> (1,3) { noteValue: Ident "A" (1,2) -> (1,3), }, @@ -787,32 +800,32 @@ Score (1,1) -> (1,3) { } `; -exports[`AlphaTexParserTest errors at201 type: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at202 > beat duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at201 type: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at202 > beat duration > parser-diagnostics 1`] = ` +[ Map { - "code" => 201, + "code" => 202, "severity" => 2, - "message" => "Missing duration value after ':'.", + "message" => "Unexpected 'Ident' token. Expected one of following: Number", "start" => Map { - "col" => 2, + "col" => 7, "line" => 1, - "offset" => 1, + "offset" => 6, }, }, ] `; -exports[`AlphaTexParserTest errors at202 beat duration 1`] = ` +exports[`AlphaTexParserTest > errors > at202 > beat duration 1`] = ` Score (1,1) -> (1,6) { - bars: Array [ + bars: [ Bar (1,1) -> (1,6) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { notes: NoteList (1,1) -> (1,5) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -827,73 +840,28 @@ Score (1,1) -> (1,6) { } `; -exports[`AlphaTexParserTest errors at202 beat duration: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at202 > note string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at202 beat duration: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at202 > note string > parser-diagnostics 1`] = ` +[ Map { "code" => 202, "severity" => 2, "message" => "Unexpected 'Ident' token. Expected one of following: Number", "start" => Map { - "col" => 7, - "line" => 1, - "offset" => 6, - }, - }, -] -`; - -exports[`AlphaTexParserTest errors at202 meta value 1`] = ` -Score (1,1) -> (1,10) { - bars: Array [ - Bar (1,1) -> (1,10) { - metaData: Array [ - Meta (1,1) -> (1,7) { - tag: Tag "title" (1,1) -> (1,7) { - prefix: Backslash (1,1) -> (1,2), - tag: Ident "title" (1,2) -> (1,7), - }, - }, - ], - beats: Array [ - Beat (1,8) -> (1,10) { - notes: NoteList (1,8) -> (1,10) { - notes: Array [ - Note (1,8) -> (1,10) { - noteValue: Number "10" (1,8) -> (1,10), - }, - ], - }, - }, - ], - }, - ], -} -`; - -exports[`AlphaTexParserTest errors at202 meta value: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest errors at202 meta value: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 202, - "severity" => 2, - "message" => "Unexpected 'Number' token. Expected one of following: String,Ident", - "start" => Map { - "col" => 10, + "col" => 4, "line" => 1, - "offset" => 9, + "offset" => 3, }, }, ] `; -exports[`AlphaTexParserTest errors at202 note string 1`] = ` +exports[`AlphaTexParserTest > errors > at202 > note string 1`] = ` Score (1,1) -> (1,3) { - bars: Array [ + bars: [ Bar (1,1) -> (1,3) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3), ], }, @@ -901,44 +869,10 @@ Score (1,1) -> (1,3) { } `; -exports[`AlphaTexParserTest errors at202 note string: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest errors at202 note string: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 202, - "severity" => 2, - "message" => "Unexpected 'Ident' token. Expected one of following: Number", - "start" => Map { - "col" => 4, - "line" => 1, - "offset" => 3, - }, - }, -] -`; - -exports[`AlphaTexParserTest errors at202 note value 1`] = ` -Score (1,1) -> (1,5) { - bars: Array [ - Bar (1,2) -> (1,5) { - beats: Array [ - Beat (1,4) -> (1,5) { - notes: NoteList (1,4) -> (1,5) { - openParenthesis: LParen (1,4) -> (1,5), - notes: Array [], - }, - }, - ], - }, - ], -} -`; - -exports[`AlphaTexParserTest errors at202 note value: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at202 > note value > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at202 note value: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at202 > note value > parser-diagnostics 1`] = ` +[ Map { "code" => 202, "severity" => 2, @@ -962,20 +896,15 @@ Array [ ] `; -exports[`AlphaTexParserTest errors at202 value list 1`] = ` -Score (1,1) -> (1,19) { - bars: Array [ - Bar (1,1) -> (1,19) { - metaData: Array [ - Meta (1,1) -> (1,19) { - tag: Tag "meta" (1,1) -> (1,6) { - prefix: Backslash (1,1) -> (1,2), - tag: Ident "meta" (1,2) -> (1,6), - }, - arguments: Arguments (1,7) -> (1,19) { - openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [], - closeParenthesis: RParen (1,18) -> (1,19), +exports[`AlphaTexParserTest > errors > at202 > note value 1`] = ` +Score (1,1) -> (1,5) { + bars: [ + Bar (1,2) -> (1,5) { + beats: [ + Beat (1,4) -> (1,5) { + notes: NoteList (1,4) -> (1,5) { + openParenthesis: LParen (1,4) -> (1,5), + notes: [], }, }, ], @@ -984,28 +913,28 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest errors at202 value list: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at203 > meta value > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at202 value list: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at203 > meta value > parser-diagnostics 1`] = ` +[ Map { - "code" => 202, + "code" => 203, "severity" => 2, - "message" => "Unexpected 'Tag' token. Expected one of following: Ident,String,Number", + "message" => "Unexpected end of file.", "start" => Map { - "col" => 18, + "col" => 7, "line" => 1, - "offset" => 17, + "offset" => 6, }, }, ] `; -exports[`AlphaTexParserTest errors at203 meta value 1`] = ` +exports[`AlphaTexParserTest > errors > at203 > meta value 1`] = ` Score (1,1) -> (1,7) { - bars: Array [ + bars: [ Bar (1,1) -> (1,7) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,7) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1018,32 +947,42 @@ Score (1,1) -> (1,7) { } `; -exports[`AlphaTexParserTest errors at203 meta value: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at203 > note string > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at203 meta value: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at203 > note string > parser-diagnostics 1`] = ` +[ Map { "code" => 203, "severity" => 2, "message" => "Unexpected end of file.", "start" => Map { - "col" => 7, + "col" => 6, "line" => 1, - "offset" => 6, + "offset" => 5, + }, + }, + Map { + "code" => 206, + "severity" => 2, + "message" => "Unexpected end of file. Group not closed.", + "start" => Map { + "col" => 6, + "line" => 1, + "offset" => 5, }, }, ] `; -exports[`AlphaTexParserTest errors at203 note string 1`] = ` +exports[`AlphaTexParserTest > errors > at203 > note string 1`] = ` Score (1,1) -> (1,6) { - bars: Array [ + bars: [ Bar (1,1) -> (1,6) { - beats: Array [ + beats: [ Beat (1,3) -> (1,6) { notes: NoteList (1,3) -> (1,6) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [], + notes: [], }, }, ], @@ -1052,38 +991,28 @@ Score (1,1) -> (1,6) { } `; -exports[`AlphaTexParserTest errors at203 note string: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at204 > bar > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at203 note string: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 203, - "severity" => 2, - "message" => "Unexpected end of file.", - "start" => Map { - "col" => 6, - "line" => 1, - "offset" => 5, - }, - }, +exports[`AlphaTexParserTest > errors > at204 > bar > parser-diagnostics 1`] = ` +[ Map { - "code" => 206, + "code" => 204, "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", + "message" => "Unrecognized metadata 'unknown'.", "start" => Map { - "col" => 6, + "col" => 11, "line" => 1, - "offset" => 5, + "offset" => 10, }, }, ] `; -exports[`AlphaTexParserTest errors at204 bar 1`] = ` +exports[`AlphaTexParserTest > errors > at204 > bar 1`] = ` Score (1,1) -> (1,11) { - bars: Array [ + bars: [ Bar (1,1) -> (1,11) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,11) { tag: Tag "unknown" (1,3) -> (1,11) { prefix: Backslash (1,3) -> (1,4), @@ -1096,28 +1025,28 @@ Score (1,1) -> (1,11) { } `; -exports[`AlphaTexParserTest errors at204 bar: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at204 > score > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at204 bar: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at204 > score > parser-diagnostics 1`] = ` +[ Map { "code" => 204, "severity" => 2, "message" => "Unrecognized metadata 'unknown'.", "start" => Map { - "col" => 11, + "col" => 9, "line" => 1, - "offset" => 10, + "offset" => 8, }, }, ] `; -exports[`AlphaTexParserTest errors at204 score 1`] = ` +exports[`AlphaTexParserTest > errors > at204 > score 1`] = ` Score (1,1) -> (1,9) { - bars: Array [ + bars: [ Bar (1,1) -> (1,9) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,9) { tag: Tag "unknown" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), @@ -1130,49 +1059,49 @@ Score (1,1) -> (1,9) { } `; -exports[`AlphaTexParserTest errors at204 score: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at205 > bar > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at204 score: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at205 > bar > parser-diagnostics 1`] = ` +[ Map { - "code" => 204, + "code" => 205, "severity" => 2, - "message" => "Unrecognized metadata 'unknown'.", + "message" => "Unrecognized property 'unknown'.", "start" => Map { - "col" => 9, + "col" => 25, "line" => 1, - "offset" => 8, + "offset" => 24, }, }, ] `; -exports[`AlphaTexParserTest errors at205 bar 1`] = ` +exports[`AlphaTexParserTest > errors > at205 > bar 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,26) { tag: Tag "track" (1,3) -> (1,9) { prefix: Backslash (1,3) -> (1,4), tag: Ident "track" (1,4) -> (1,9), }, arguments: Arguments (1,10) -> (1,15) { - arguments: Array [ + arguments: [ String "Test" (1,10) -> (1,15) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, properties: Props (1,17) -> (1,26) { openBrace: LBrace (1,17) -> (1,18), - properties: Array [ + properties: [ Prop (1,18) -> (1,25) { property: Ident "unknown" (1,18) -> (1,25), }, @@ -1186,47 +1115,47 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParserTest errors at205 bar: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at205 > barre > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at205 bar: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at205 > barre > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, - "message" => "Unrecognized property 'unknown'.", + "message" => "Unrecognized property 'invalid'.", "start" => Map { - "col" => 25, + "col" => 20, "line" => 1, - "offset" => 24, + "offset" => 19, }, }, ] `; -exports[`AlphaTexParserTest errors at205 barre 1`] = ` +exports[`AlphaTexParserTest > errors > at205 > barre 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - beats: Array [ + beats: [ Beat (1,1) -> (1,21) { notes: NoteList (1,1) -> (1,21) { - notes: Array [ + notes: [ Note (1,1) -> (1,21) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,21) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,12) { property: Ident "barre" (1,5) -> (1,10), properties: Arguments (1,11) -> (1,12) { - arguments: Array [ + arguments: [ Number "1" (1,11) -> (1,12) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1248,32 +1177,32 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParserTest errors at205 barre: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at205 > beat > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at205 barre: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at205 > beat > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, - "message" => "Unrecognized property 'invalid'.", + "message" => "Unrecognized property 'unknown'.", "start" => Map { - "col" => 20, + "col" => 16, "line" => 1, - "offset" => 19, + "offset" => 15, }, }, ] `; -exports[`AlphaTexParserTest errors at205 beat 1`] = ` +exports[`AlphaTexParserTest > errors > at205 > beat 1`] = ` Score (1,1) -> (1,17) { - bars: Array [ + bars: [ Bar (1,1) -> (1,17) { - beats: Array [ + beats: [ Beat (1,3) -> (1,17) { notes: NoteList (1,3) -> (1,7) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -1282,7 +1211,7 @@ Score (1,1) -> (1,17) { }, beatEffects: Props (1,8) -> (1,17) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,9) -> (1,16) { property: Ident "unknown" (1,9) -> (1,16), }, @@ -1296,94 +1225,36 @@ Score (1,1) -> (1,17) { } `; -exports[`AlphaTexParserTest errors at205 beat: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest errors at205 beat: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 205, - "severity" => 2, - "message" => "Unrecognized property 'unknown'.", - "start" => Map { - "col" => 16, - "line" => 1, - "offset" => 15, - }, - }, -] -`; - -exports[`AlphaTexParserTest errors at205 fermata 1`] = ` -Score (1,1) -> (1,20) { - bars: Array [ - Bar (1,1) -> (1,20) { - beats: Array [ - Beat (1,1) -> (1,20) { - notes: NoteList (1,1) -> (1,20) { - notes: Array [ - Note (1,1) -> (1,20) { - noteValue: Ident "C4" (1,1) -> (1,3), - noteEffects: Props (1,4) -> (1,20) { - openBrace: LBrace (1,4) -> (1,5), - properties: Array [ - Prop (1,5) -> (1,12) { - property: Ident "fermata" (1,5) -> (1,12), - }, - Prop (1,13) -> (1,20) { - property: Ident "invalid" (1,13) -> (1,20), - }, - ], - }, - }, - ], - }, - }, - ], - }, - ], -} -`; - -exports[`AlphaTexParserTest errors at205 fermata: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at205 > gracetype > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at205 fermata: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at205 > gracetype > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, "message" => "Unrecognized property 'invalid'.", "start" => Map { - "col" => 20, - "line" => 1, - "offset" => 19, - }, - }, - Map { - "code" => 206, - "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", - "start" => Map { - "col" => 20, + "col" => 15, "line" => 1, - "offset" => 19, + "offset" => 14, }, }, ] `; -exports[`AlphaTexParserTest errors at205 gracetype 1`] = ` +exports[`AlphaTexParserTest > errors > at205 > gracetype 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,16) { notes: NoteList (1,1) -> (1,16) { - notes: Array [ + notes: [ Note (1,1) -> (1,16) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,16) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,7) { property: Ident "gr" (1,5) -> (1,7), }, @@ -1403,37 +1274,37 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest errors at205 gracetype: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at205 > note > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at205 gracetype: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at205 > note > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, - "message" => "Unrecognized property 'invalid'.", + "message" => "Unrecognized property 'unknown'.", "start" => Map { - "col" => 15, + "col" => 14, "line" => 1, - "offset" => 14, + "offset" => 13, }, }, ] `; -exports[`AlphaTexParserTest errors at205 note 1`] = ` +exports[`AlphaTexParserTest > errors > at205 > note 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,3) -> (1,16) { notes: NoteList (1,3) -> (1,16) { openParenthesis: LParen (1,3) -> (1,4), - notes: Array [ + notes: [ Note (1,4) -> (1,15) { noteValue: Ident "C4" (1,4) -> (1,6), noteEffects: Props (1,6) -> (1,15) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,14) { property: Ident "unknown" (1,7) -> (1,14), }, @@ -1451,32 +1322,32 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest errors at205 note: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at206 > note list > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at205 note: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at206 > note list > parser-diagnostics 1`] = ` +[ Map { - "code" => 205, + "code" => 206, "severity" => 2, - "message" => "Unrecognized property 'unknown'.", + "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 14, + "col" => 4, "line" => 1, - "offset" => 13, + "offset" => 3, }, }, ] `; -exports[`AlphaTexParserTest errors at206 note list 1`] = ` +exports[`AlphaTexParserTest > errors > at206 > note list 1`] = ` Score (1,1) -> (1,4) { - bars: Array [ + bars: [ Bar (1,1) -> (1,4) { - beats: Array [ + beats: [ Beat (1,1) -> (1,4) { notes: NoteList (1,1) -> (1,4) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -1489,80 +1360,28 @@ Score (1,1) -> (1,4) { } `; -exports[`AlphaTexParserTest errors at206 note list: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest errors at206 note list: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 206, - "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", - "start" => Map { - "col" => 4, - "line" => 1, - "offset" => 3, - }, - }, -] -`; - -exports[`AlphaTexParserTest errors at206 properties 1`] = ` -Score (1,1) -> (1,26) { - bars: Array [ - Bar (1,1) -> (1,26) { - metaData: Array [ - Meta (1,1) -> (1,26) { - tag: Tag "track" (1,1) -> (1,7) { - prefix: Backslash (1,1) -> (1,2), - tag: Ident "track" (1,2) -> (1,7), - }, - arguments: Arguments (1,8) -> (1,13) { - arguments: Array [ - String "Test" (1,8) -> (1,13), - ], - }, - properties: Props (1,15) -> (1,26) { - openBrace: LBrace (1,15) -> (1,16), - properties: Array [ - Prop (1,17) -> (1,26) { - property: Ident "color" (1,17) -> (1,22), - properties: Arguments (1,23) -> (1,26) { - arguments: Array [ - Ident "red" (1,23) -> (1,26), - ], - }, - }, - ], - }, - }, - ], - }, - ], -} -`; - -exports[`AlphaTexParserTest errors at206 properties: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > errors > at206 > values > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest errors at206 properties: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > errors > at206 > values > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 26, + "col" => 15, "line" => 1, - "offset" => 25, + "offset" => 14, }, }, ] `; -exports[`AlphaTexParserTest errors at206 values 1`] = ` +exports[`AlphaTexParserTest > errors > at206 > values 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -1570,7 +1389,7 @@ Score (1,1) -> (1,14) { }, arguments: Arguments (1,8) -> (1,14) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ String "Test" (1,9) -> (1,14), ], }, @@ -1581,52 +1400,15 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParserTest errors at206 values: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest errors at206 values: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 206, - "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", - "start" => Map { - "col" => 15, - "line" => 1, - "offset" => 14, - }, - }, -] -`; +exports[`AlphaTexParserTest > floats > tempo parenthesis > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest floats tempo 1`] = ` -Score (1,1) -> (1,28) { - bars: Array [ - Bar (1,1) -> (1,28) { - metaData: Array [ - Meta (1,3) -> (1,28) { - tag: Tag "tempo" (1,3) -> (1,9) { - prefix: Backslash (1,3) -> (1,4), - tag: Ident "tempo" (1,4) -> (1,9), - }, - arguments: Arguments (1,10) -> (1,28) { - arguments: Array [ - Number "120" (1,10) -> (1,13), - String "Moderate" (1,14) -> (1,23), - Number "0.5" (1,25) -> (1,28), - ], - }, - }, - ], - }, - ], -} -`; +exports[`AlphaTexParserTest > floats > tempo parenthesis > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest floats tempo parenthesis 1`] = ` +exports[`AlphaTexParserTest > floats > tempo parenthesis 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,30) { tag: Tag "tempo" (1,3) -> (1,9) { prefix: Backslash (1,3) -> (1,4), @@ -1634,7 +1416,7 @@ Score (1,1) -> (1,30) { }, arguments: Arguments (1,10) -> (1,30) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Number "120" (1,11) -> (1,14), String "Moderate" (1,15) -> (1,24), Number "0.5" (1,26) -> (1,29), @@ -1648,33 +1430,29 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest floats tempo parenthesis: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest floats tempo parenthesis: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest floats tempo: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > floats > valuelist > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest floats tempo: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > floats > valuelist > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest floats valuelist 1`] = ` +exports[`AlphaTexParserTest > floats > valuelist 1`] = ` Score (1,1) -> (1,13) { - bars: Array [ + bars: [ Bar (1,1) -> (1,13) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,13) { tag: Tag "scale" (1,3) -> (1,9) { prefix: Backslash (1,3) -> (1,4), tag: Ident "scale" (1,4) -> (1,9), }, arguments: Arguments (1,10) -> (1,13) { - arguments: Array [ + arguments: [ Number "0.5" (1,10) -> (1,13) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1686,11 +1464,15 @@ Score (1,1) -> (1,13) { } `; -exports[`AlphaTexParserTest floats valuelist parenthesis 1`] = ` +exports[`AlphaTexParserTest > floats > valuelist parenthesis > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > floats > valuelist parenthesis > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > floats > valuelist parenthesis 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,19) { tag: Tag "unknown" (1,1) -> (1,9) { prefix: Backslash (1,1) -> (1,2), @@ -1698,7 +1480,7 @@ Score (1,1) -> (1,19) { }, arguments: Arguments (1,10) -> (1,19) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Number "1.2" (1,11) -> (1,14), Number "2.3" (1,15) -> (1,18), ], @@ -1711,59 +1493,68 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest floats valuelist parenthesis: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest floats valuelist parenthesis: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > finished beat effect value, not closed > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest floats valuelist: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest floats valuelist: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > finished beat effect value, not closed > parser-diagnostics 1`] = ` +[ + Map { + "code" => 206, + "severity" => 2, + "message" => "Unexpected end of file. Group not closed.", + "start" => Map { + "col" => 51, + "line" => 1, + "offset" => 50, + }, + }, +] +`; -exports[`AlphaTexParserTest intermediate finished beat effect value, not closed 1`] = ` +exports[`AlphaTexParserTest > intermediate > finished beat effect value, not closed 1`] = ` Score (1,1) -> (1,49) { - bars: Array [ + bars: [ Bar (1,1) -> (1,49) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,49) { notes: NoteList (1,18) -> (1,34) { - notes: Array [ + notes: [ Note (1,18) -> (1,34) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,34) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1779,18 +1570,18 @@ Score (1,1) -> (1,49) { durationValue: Number "4" (1,37) -> (1,38), beatEffects: Props (1,39) -> (1,49) { openBrace: LBrace (1,39) -> (1,40), - properties: Array [ + properties: [ Prop (1,41) -> (1,49) { property: Ident "rasg" (1,41) -> (1,45), properties: Arguments (1,46) -> (1,49) { - arguments: Array [ + arguments: [ String "ii" (1,46) -> (1,49) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1805,60 +1596,60 @@ Score (1,1) -> (1,49) { } `; -exports[`AlphaTexParserTest intermediate finished beat effect value, not closed: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > finished meta property value, not closed > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate finished beat effect value, not closed: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > finished meta property value, not closed > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 51, + "col" => 25, "line" => 1, - "offset" => 50, + "offset" => 24, }, }, ] `; -exports[`AlphaTexParserTest intermediate finished meta property value, not closed 1`] = ` +exports[`AlphaTexParserTest > intermediate > finished meta property value, not closed 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,24) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,10) { - arguments: Array [ + arguments: [ String "X" (1,8) -> (1,10) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, properties: Props (1,12) -> (1,24) { openBrace: LBrace (1,12) -> (1,13), - properties: Array [ + properties: [ Prop (1,14) -> (1,24) { property: Ident "color" (1,14) -> (1,19), properties: Arguments (1,20) -> (1,24) { - arguments: Array [ + arguments: [ String "red" (1,20) -> (1,24) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1873,68 +1664,68 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest intermediate finished meta property value, not closed : lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > finished note effect value, not closed > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate finished meta property value, not closed : parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > finished note effect value, not closed > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 25, + "col" => 32, "line" => 1, - "offset" => 24, + "offset" => 31, }, }, ] `; -exports[`AlphaTexParserTest intermediate finished note effect value, not closed 1`] = ` +exports[`AlphaTexParserTest > intermediate > finished note effect value, not closed 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,31) { notes: NoteList (1,18) -> (1,31) { - notes: Array [ + notes: [ Note (1,18) -> (1,31) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,31) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -1952,68 +1743,55 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParserTest intermediate finished note effect value, not closed: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started beat duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate finished note effect value, not closed: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 206, - "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", - "start" => Map { - "col" => 32, - "line" => 1, - "offset" => 31, - }, - }, -] -`; +exports[`AlphaTexParserTest > intermediate > started beat duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started beat duration 1`] = ` +exports[`AlphaTexParserTest > intermediate > started beat duration 1`] = ` Score (1,1) -> (1,36) { - bars: Array [ + bars: [ Bar (1,1) -> (1,36) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,36) { notes: NoteList (1,18) -> (1,34) { - notes: Array [ + notes: [ Note (1,18) -> (1,34) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,34) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2033,55 +1811,78 @@ Score (1,1) -> (1,36) { } `; -exports[`AlphaTexParserTest intermediate started beat duration: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started beat effect name > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started beat duration: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started beat effect name > parser-diagnostics 1`] = ` +[ + Map { + "code" => 205, + "severity" => 2, + "message" => "Unrecognized property 'ras'.", + "start" => Map { + "col" => 44, + "line" => 1, + "offset" => 43, + }, + }, + Map { + "code" => 206, + "severity" => 2, + "message" => "Unexpected end of file. Group not closed.", + "start" => Map { + "col" => 44, + "line" => 1, + "offset" => 43, + }, + }, +] +`; -exports[`AlphaTexParserTest intermediate started beat effect name 1`] = ` +exports[`AlphaTexParserTest > intermediate > started beat effect name 1`] = ` Score (1,1) -> (1,44) { - bars: Array [ + bars: [ Bar (1,1) -> (1,44) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,44) { notes: NoteList (1,18) -> (1,34) { - notes: Array [ + notes: [ Note (1,18) -> (1,34) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,34) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2097,7 +1898,7 @@ Score (1,1) -> (1,44) { durationValue: Number "4" (1,37) -> (1,38), beatEffects: Props (1,39) -> (1,44) { openBrace: LBrace (1,39) -> (1,40), - properties: Array [ + properties: [ Prop (1,41) -> (1,44) { property: Ident "ras" (1,41) -> (1,44), }, @@ -2110,18 +1911,31 @@ Score (1,1) -> (1,44) { } `; -exports[`AlphaTexParserTest intermediate started beat effect name: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started beat effect value > lexer-diagnostics 1`] = ` +[ + Map { + "code" => 6, + "severity" => 2, + "message" => "Unexpected end of file. String not closed.", + "start" => Map { + "col" => 48, + "line" => 1, + "offset" => 47, + }, + }, +] +`; -exports[`AlphaTexParserTest intermediate started beat effect name: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started beat effect value > parser-diagnostics 1`] = ` +[ Map { - "code" => 205, + "code" => 203, "severity" => 2, - "message" => "Unrecognized property 'ras'.", + "message" => "Unexpected end of file.", "start" => Map { - "col" => 44, + "col" => 48, "line" => 1, - "offset" => 43, + "offset" => 47, }, }, Map { @@ -2129,59 +1943,59 @@ Array [ "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 44, + "col" => 48, "line" => 1, - "offset" => 43, + "offset" => 47, }, }, ] `; -exports[`AlphaTexParserTest intermediate started beat effect value 1`] = ` +exports[`AlphaTexParserTest > intermediate > started beat effect value 1`] = ` Score (1,1) -> (1,45) { - bars: Array [ + bars: [ Bar (1,1) -> (1,45) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,45) { notes: NoteList (1,18) -> (1,34) { - notes: Array [ + notes: [ Note (1,18) -> (1,34) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,34) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2197,7 +2011,7 @@ Score (1,1) -> (1,45) { durationValue: Number "4" (1,37) -> (1,38), beatEffects: Props (1,39) -> (1,45) { openBrace: LBrace (1,39) -> (1,40), - properties: Array [ + properties: [ Prop (1,41) -> (1,45) { property: Ident "rasg" (1,41) -> (1,45), }, @@ -2210,91 +2024,68 @@ Score (1,1) -> (1,45) { } `; -exports[`AlphaTexParserTest intermediate started beat effect value: lexer-diagnostics 1`] = ` -Array [ - Map { - "code" => 6, - "severity" => 2, - "message" => "Unexpected end of file. String not closed.", - "start" => Map { - "col" => 48, - "line" => 1, - "offset" => 47, - }, - }, -] -`; +exports[`AlphaTexParserTest > intermediate > started beat effects > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started beat effect value: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 203, - "severity" => 2, - "message" => "Unexpected end of file.", - "start" => Map { - "col" => 48, - "line" => 1, - "offset" => 47, - }, - }, +exports[`AlphaTexParserTest > intermediate > started beat effects > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 48, + "col" => 41, "line" => 1, - "offset" => 47, + "offset" => 40, }, }, ] `; -exports[`AlphaTexParserTest intermediate started beat effects 1`] = ` +exports[`AlphaTexParserTest > intermediate > started beat effects 1`] = ` Score (1,1) -> (1,40) { - bars: Array [ + bars: [ Bar (1,1) -> (1,40) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,40) { notes: NoteList (1,18) -> (1,34) { - notes: Array [ + notes: [ Note (1,18) -> (1,34) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,34) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,31) { property: Ident "slur" (1,23) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ String "S1" (1,28) -> (1,31) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2310,7 +2101,7 @@ Score (1,1) -> (1,40) { durationValue: Number "4" (1,37) -> (1,38), beatEffects: Props (1,39) -> (1,40) { openBrace: LBrace (1,39) -> (1,40), - properties: Array [], + properties: [], }, }, ], @@ -2319,47 +2110,47 @@ Score (1,1) -> (1,40) { } `; -exports[`AlphaTexParserTest intermediate started beat effects: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest intermediate started beat effects: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started initial meta > lexer-diagnostics 1`] = ` +[ Map { - "code" => 206, + "code" => 2, "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", + "message" => "Missing identifier after meta data start", "start" => Map { - "col" => 41, + "col" => 2, "line" => 1, - "offset" => 40, + "offset" => 1, }, }, ] `; -exports[`AlphaTexParserTest intermediate started initial meta 1`] = `Score (1,1) -> (1,2)`; +exports[`AlphaTexParserTest > intermediate > started initial meta > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > intermediate > started initial meta 1`] = `Score (1,1) -> (1,2)`; -exports[`AlphaTexParserTest intermediate started initial meta: lexer-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > intermediate > started meta > parser-diagnostics 1`] = ` +[ Map { - "code" => 2, + "code" => 204, "severity" => 2, - "message" => "Missing identifier after meta data start", + "message" => "Unrecognized metadata 'tr'.", "start" => Map { - "col" => 2, + "col" => 4, "line" => 1, - "offset" => 1, + "offset" => 3, }, }, ] `; -exports[`AlphaTexParserTest intermediate started initial meta: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest intermediate started meta 1`] = ` +exports[`AlphaTexParserTest > intermediate > started meta 1`] = ` Score (1,1) -> (1,4) { - bars: Array [ + bars: [ Bar (1,1) -> (1,4) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,4) { tag: Tag "tr" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), @@ -2372,44 +2163,10 @@ Score (1,1) -> (1,4) { } `; -exports[`AlphaTexParserTest intermediate started meta properties 1`] = ` -Score (1,1) -> (1,13) { - bars: Array [ - Bar (1,1) -> (1,13) { - metaData: Array [ - Meta (1,1) -> (1,13) { - tag: Tag "track" (1,1) -> (1,7) { - prefix: Backslash (1,1) -> (1,2), - tag: Ident "track" (1,2) -> (1,7), - }, - arguments: Arguments (1,8) -> (1,10) { - arguments: Array [ - String "X" (1,8) -> (1,10) { - parameterIndices: Map { - 0 => 0, - }, - }, - ], - signatureCandidateIndices: Array [ - 0, - ], - validated: true, - }, - properties: Props (1,12) -> (1,13) { - openBrace: LBrace (1,12) -> (1,13), - properties: Array [], - }, - }, - ], - }, - ], -} -`; - -exports[`AlphaTexParserTest intermediate started meta properties: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started meta properties > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started meta properties: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta properties > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, @@ -2423,36 +2180,32 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta property name 1`] = ` -Score (1,1) -> (1,16) { - bars: Array [ - Bar (1,1) -> (1,16) { - metaData: Array [ - Meta (1,1) -> (1,16) { +exports[`AlphaTexParserTest > intermediate > started meta properties 1`] = ` +Score (1,1) -> (1,13) { + bars: [ + Bar (1,1) -> (1,13) { + metaData: [ + Meta (1,1) -> (1,13) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,10) { - arguments: Array [ + arguments: [ String "X" (1,8) -> (1,10) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, - properties: Props (1,12) -> (1,16) { + properties: Props (1,12) -> (1,13) { openBrace: LBrace (1,12) -> (1,13), - properties: Array [ - Prop (1,14) -> (1,16) { - property: Ident "co" (1,14) -> (1,16), - }, - ], + properties: [], }, }, ], @@ -2461,10 +2214,10 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest intermediate started meta property name: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started meta property name > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started meta property name: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta property name > parser-diagnostics 1`] = ` +[ Map { "code" => 205, "severity" => 2, @@ -2488,34 +2241,34 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta property value 1`] = ` -Score (1,1) -> (1,19) { - bars: Array [ - Bar (1,1) -> (1,19) { - metaData: Array [ - Meta (1,1) -> (1,19) { +exports[`AlphaTexParserTest > intermediate > started meta property name 1`] = ` +Score (1,1) -> (1,16) { + bars: [ + Bar (1,1) -> (1,16) { + metaData: [ + Meta (1,1) -> (1,16) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,10) { - arguments: Array [ + arguments: [ String "X" (1,8) -> (1,10) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, - properties: Props (1,12) -> (1,19) { + properties: Props (1,12) -> (1,16) { openBrace: LBrace (1,12) -> (1,13), - properties: Array [ - Prop (1,14) -> (1,19) { - property: Ident "color" (1,14) -> (1,19), + properties: [ + Prop (1,14) -> (1,16) { + property: Ident "co" (1,14) -> (1,16), }, ], }, @@ -2526,8 +2279,8 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest intermediate started meta property value: lexer-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta property value > lexer-diagnostics 1`] = ` +[ Map { "code" => 6, "severity" => 2, @@ -2541,8 +2294,8 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta property value: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta property value > parser-diagnostics 1`] = ` +[ Map { "code" => 203, "severity" => 2, @@ -2566,15 +2319,36 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta string 1`] = ` -Score (1,1) -> (1,7) { - bars: Array [ - Bar (1,1) -> (1,7) { - metaData: Array [ - Meta (1,1) -> (1,7) { - tag: Tag "title" (1,1) -> (1,7) { +exports[`AlphaTexParserTest > intermediate > started meta property value 1`] = ` +Score (1,1) -> (1,19) { + bars: [ + Bar (1,1) -> (1,19) { + metaData: [ + Meta (1,1) -> (1,19) { + tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), - tag: Ident "title" (1,2) -> (1,7), + tag: Ident "track" (1,2) -> (1,7), + }, + arguments: Arguments (1,8) -> (1,10) { + arguments: [ + String "X" (1,8) -> (1,10) { + parameterIndices: Map { + 0 => 0, + }, + }, + ], + signatureCandidateIndices: [ + 0, + ], + validated: true, + }, + properties: Props (1,12) -> (1,19) { + openBrace: LBrace (1,12) -> (1,13), + properties: [ + Prop (1,14) -> (1,19) { + property: Ident "color" (1,14) -> (1,19), + }, + ], }, }, ], @@ -2583,8 +2357,8 @@ Score (1,1) -> (1,7) { } `; -exports[`AlphaTexParserTest intermediate started meta string: lexer-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta string > lexer-diagnostics 1`] = ` +[ Map { "code" => 6, "severity" => 2, @@ -2598,8 +2372,8 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta string: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started meta string > parser-diagnostics 1`] = ` +[ Map { "code" => 203, "severity" => 2, @@ -2613,57 +2387,84 @@ Array [ ] `; -exports[`AlphaTexParserTest intermediate started meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started meta string 1`] = ` +Score (1,1) -> (1,7) { + bars: [ + Bar (1,1) -> (1,7) { + metaData: [ + Meta (1,1) -> (1,7) { + tag: Tag "title" (1,1) -> (1,7) { + prefix: Backslash (1,1) -> (1,2), + tag: Ident "title" (1,2) -> (1,7), + }, + }, + ], + }, + ], +} +`; + +exports[`AlphaTexParserTest > intermediate > started note effect name > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started meta: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started note effect name > parser-diagnostics 1`] = ` +[ Map { - "code" => 204, + "code" => 205, "severity" => 2, - "message" => "Unrecognized metadata 'tr'.", + "message" => "Unrecognized property 'slu'.", "start" => Map { - "col" => 4, + "col" => 26, "line" => 1, - "offset" => 3, + "offset" => 25, + }, + }, + Map { + "code" => 206, + "severity" => 2, + "message" => "Unexpected end of file. Group not closed.", + "start" => Map { + "col" => 26, + "line" => 1, + "offset" => 25, }, }, ] `; -exports[`AlphaTexParserTest intermediate started note effect name 1`] = ` +exports[`AlphaTexParserTest > intermediate > started note effect name 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,26) { notes: NoteList (1,18) -> (1,26) { - notes: Array [ + notes: [ Note (1,18) -> (1,26) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,26) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,26) { property: Ident "slu" (1,23) -> (1,26), }, @@ -2679,18 +2480,31 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParserTest intermediate started note effect name: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started note effect value > lexer-diagnostics 1`] = ` +[ + Map { + "code" => 6, + "severity" => 2, + "message" => "Unexpected end of file. String not closed.", + "start" => Map { + "col" => 31, + "line" => 1, + "offset" => 30, + }, + }, +] +`; -exports[`AlphaTexParserTest intermediate started note effect name: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > intermediate > started note effect value > parser-diagnostics 1`] = ` +[ Map { - "code" => 205, + "code" => 203, "severity" => 2, - "message" => "Unrecognized property 'slu'.", + "message" => "Unexpected end of file.", "start" => Map { - "col" => 26, + "col" => 31, "line" => 1, - "offset" => 25, + "offset" => 30, }, }, Map { @@ -2698,48 +2512,48 @@ Array [ "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 26, + "col" => 31, "line" => 1, - "offset" => 25, + "offset" => 30, }, }, ] `; -exports[`AlphaTexParserTest intermediate started note effect value 1`] = ` +exports[`AlphaTexParserTest > intermediate > started note effect value 1`] = ` Score (1,1) -> (1,27) { - bars: Array [ + bars: [ Bar (1,1) -> (1,27) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,27) { notes: NoteList (1,18) -> (1,27) { - notes: Array [ + notes: [ Note (1,18) -> (1,27) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,27) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,27) { property: Ident "slur" (1,23) -> (1,27), }, @@ -2755,80 +2569,57 @@ Score (1,1) -> (1,27) { } `; -exports[`AlphaTexParserTest intermediate started note effect value: lexer-diagnostics 1`] = ` -Array [ - Map { - "code" => 6, - "severity" => 2, - "message" => "Unexpected end of file. String not closed.", - "start" => Map { - "col" => 31, - "line" => 1, - "offset" => 30, - }, - }, -] -`; +exports[`AlphaTexParserTest > intermediate > started note effects > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started note effect value: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 203, - "severity" => 2, - "message" => "Unexpected end of file.", - "start" => Map { - "col" => 31, - "line" => 1, - "offset" => 30, - }, - }, +exports[`AlphaTexParserTest > intermediate > started note effects > parser-diagnostics 1`] = ` +[ Map { "code" => 206, "severity" => 2, "message" => "Unexpected end of file. Group not closed.", "start" => Map { - "col" => 31, + "col" => 23, "line" => 1, - "offset" => 30, + "offset" => 22, }, }, ] `; -exports[`AlphaTexParserTest intermediate started note effects 1`] = ` +exports[`AlphaTexParserTest > intermediate > started note effects 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,1) -> (1,22) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,22) { notes: NoteList (1,18) -> (1,22) { - notes: Array [ + notes: [ Note (1,18) -> (1,22) { noteValue: Ident "C4" (1,18) -> (1,20), noteEffects: Props (1,21) -> (1,22) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [], + properties: [], }, }, ], @@ -2840,52 +2631,39 @@ Score (1,1) -> (1,22) { } `; -exports[`AlphaTexParserTest intermediate started note effects: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > intermediate > started pitched note > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started note effects: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 206, - "severity" => 2, - "message" => "Unexpected end of file. Group not closed.", - "start" => Map { - "col" => 23, - "line" => 1, - "offset" => 22, - }, - }, -] -`; +exports[`AlphaTexParserTest > intermediate > started pitched note > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started pitched note 1`] = ` +exports[`AlphaTexParserTest > intermediate > started pitched note 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,19) { notes: NoteList (1,18) -> (1,19) { - notes: Array [ + notes: [ Note (1,18) -> (1,19) { noteValue: Ident "C" (1,18) -> (1,19), }, @@ -2898,34 +2676,60 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest intermediate started pitched note: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > recovery > props > additional values > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest intermediate started pitched note: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > recovery > props > additional values > parser-diagnostics 1`] = ` +[ + Map { + "code" => 220, + "severity" => 2, + "message" => "Error parsing arguments: unexpected additional arguments. Signatures: +(String) +(Ident)", + "start" => Map { + "col" => 22, + "line" => 1, + "offset" => 21, + }, + }, + Map { + "code" => 220, + "severity" => 2, + "message" => "Error parsing arguments: unexpected additional arguments. Signatures: +()", + "start" => Map { + "col" => 26, + "line" => 1, + "offset" => 25, + }, + }, +] +`; -exports[`AlphaTexParserTest recovery props additional values 1`] = ` +exports[`AlphaTexParserTest > recovery > props > additional values 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - beats: Array [ + beats: [ Beat (1,1) -> (1,30) { notes: NoteList (1,1) -> (1,30) { - notes: Array [ + notes: [ Note (1,1) -> (1,30) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,30) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,22) { property: Ident "slur" (1,6) -> (1,10), properties: Arguments (1,11) -> (1,22) { - arguments: Array [ + arguments: [ String "S1" (1,11) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -2950,60 +2754,47 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest recovery props additional values: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > recovery > props > unknown property > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest recovery props additional values: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 220, - "severity" => 2, - "message" => "Error parsing arguments: unexpected additional arguments. Signatures: -(String) -(Ident)", - "start" => Map { - "col" => 22, - "line" => 1, - "offset" => 21, - }, - }, +exports[`AlphaTexParserTest > recovery > props > unknown property > parser-diagnostics 1`] = ` +[ Map { - "code" => 220, + "code" => 205, "severity" => 2, - "message" => "Error parsing arguments: unexpected additional arguments. Signatures: -()", + "message" => "Unrecognized property 'invalid'.", "start" => Map { - "col" => 26, + "col" => 42, "line" => 1, - "offset" => 25, + "offset" => 41, }, }, ] `; -exports[`AlphaTexParserTest recovery props unknown property 1`] = ` +exports[`AlphaTexParserTest > recovery > props > unknown property 1`] = ` Score (1,1) -> (1,45) { - bars: Array [ + bars: [ Bar (1,1) -> (1,45) { - beats: Array [ + beats: [ Beat (1,1) -> (1,45) { notes: NoteList (1,1) -> (1,45) { - notes: Array [ + notes: [ Note (1,1) -> (1,45) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,45) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,14) { property: Ident "slur" (1,6) -> (1,10), properties: Arguments (1,11) -> (1,14) { - arguments: Array [ + arguments: [ String "s1" (1,11) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -3028,43 +2819,44 @@ Score (1,1) -> (1,45) { } `; -exports[`AlphaTexParserTest recovery props unknown property: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > recovery > props > unknown value > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest recovery props unknown property: parser-diagnostics 1`] = ` -Array [ +exports[`AlphaTexParserTest > recovery > props > unknown value > parser-diagnostics 1`] = ` +[ Map { - "code" => 205, + "code" => 219, "severity" => 2, - "message" => "Unrecognized property 'invalid'.", + "message" => "Error parsing arguments: no overload matched arguments (String). Signatures: +(Ident|String)", "start" => Map { - "col" => 42, + "col" => 19, "line" => 1, - "offset" => 41, + "offset" => 18, }, }, ] `; -exports[`AlphaTexParserTest recovery props unknown value 1`] = ` +exports[`AlphaTexParserTest > recovery > props > unknown value 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { notes: NoteList (1,1) -> (1,24) { - notes: Array [ + notes: [ Note (1,1) -> (1,24) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,24) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,19) { property: Ident "rasg" (1,6) -> (1,10), properties: Arguments (1,11) -> (1,19) { - arguments: Array [ + arguments: [ String "invalid" (1,11) -> (1,19), ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], }, @@ -3085,36 +2877,22 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest recovery props unknown value: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > known no score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest recovery props unknown value: parser-diagnostics 1`] = ` -Array [ - Map { - "code" => 219, - "severity" => 2, - "message" => "Error parsing arguments: no overload matched arguments (String). Signatures: -(Ident|String)", - "start" => Map { - "col" => 19, - "line" => 1, - "offset" => 18, - }, - }, -] -`; +exports[`AlphaTexParserTest > valid-bar-meta > known no score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta known no score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bar-meta > known no score meta 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,13) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,8) { tag: Tag "ts" (1,1) -> (1,4) { prefix: Backslash (1,1) -> (1,2), tag: Ident "ts" (1,2) -> (1,4), }, arguments: Arguments (1,5) -> (1,8) { - arguments: Array [ + arguments: [ Number "3" (1,5) -> (1,6) { parameterIndices: Map { 1 => 0, @@ -3126,17 +2904,17 @@ Score (1,1) -> (1,16) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,9) -> (1,11) { notes: NoteList (1,9) -> (1,11) { - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C4" (1,9) -> (1,11), }, @@ -3147,10 +2925,10 @@ Score (1,1) -> (1,16) { pipe: Pipe (1,12) -> (1,13), }, Bar (1,14) -> (1,16) { - beats: Array [ + beats: [ Beat (1,14) -> (1,16) { notes: NoteList (1,14) -> (1,16) { - notes: Array [ + notes: [ Note (1,14) -> (1,16) { noteValue: Ident "C4" (1,14) -> (1,16), }, @@ -3163,22 +2941,22 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest valid-bar-meta known no score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > known score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta known no score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > known score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta known score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bar-meta > known score meta 1`] = ` Score (1,1) -> (1,29) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,10) { tag: Tag "ts" (1,3) -> (1,6) { prefix: Backslash (1,3) -> (1,4), tag: Ident "ts" (1,4) -> (1,6), }, arguments: Arguments (1,7) -> (1,10) { - arguments: Array [ + arguments: [ Number "3" (1,7) -> (1,8) { parameterIndices: Map { 1 => 0, @@ -3190,18 +2968,18 @@ Score (1,1) -> (1,29) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,12) -> (1,21) { notes: NoteList (1,12) -> (1,21) { openParenthesis: LParen (1,12) -> (1,13), - notes: Array [ + notes: [ Note (1,13) -> (1,19) { noteValue: String "Value" (1,13) -> (1,19), }, @@ -3211,7 +2989,7 @@ Score (1,1) -> (1,29) { }, Beat (1,22) -> (1,24) { notes: NoteList (1,22) -> (1,24) { - notes: Array [ + notes: [ Note (1,22) -> (1,24) { noteValue: Ident "C4" (1,22) -> (1,24), }, @@ -3222,10 +3000,10 @@ Score (1,1) -> (1,29) { pipe: Pipe (1,25) -> (1,26), }, Bar (1,27) -> (1,29) { - beats: Array [ + beats: [ Beat (1,27) -> (1,29) { notes: NoteList (1,27) -> (1,29) { - notes: Array [ + notes: [ Note (1,27) -> (1,29) { noteValue: Ident "C4" (1,27) -> (1,29), }, @@ -3238,15 +3016,15 @@ Score (1,1) -> (1,29) { } `; -exports[`AlphaTexParserTest valid-bar-meta known score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > unknown no score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta known score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > unknown no score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta unknown no score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bar-meta > unknown no score meta 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,28) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "notExisting" (1,1) -> (1,13) { prefix: Backslash (1,1) -> (1,2), @@ -3254,17 +3032,17 @@ Score (1,1) -> (1,31) { }, arguments: Arguments (1,14) -> (1,23) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ String "Value" (1,15) -> (1,21), ], closeParenthesis: RParen (1,22) -> (1,23), }, }, ], - beats: Array [ + beats: [ Beat (1,24) -> (1,26) { notes: NoteList (1,24) -> (1,26) { - notes: Array [ + notes: [ Note (1,24) -> (1,26) { noteValue: Ident "C4" (1,24) -> (1,26), }, @@ -3275,10 +3053,10 @@ Score (1,1) -> (1,31) { pipe: Pipe (1,27) -> (1,28), }, Bar (1,29) -> (1,31) { - beats: Array [ + beats: [ Beat (1,29) -> (1,31) { notes: NoteList (1,29) -> (1,31) { - notes: Array [ + notes: [ Note (1,29) -> (1,31) { noteValue: Ident "C4" (1,29) -> (1,31), }, @@ -3291,15 +3069,15 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParserTest valid-bar-meta unknown no score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > unkonwn score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta unknown no score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bar-meta > unkonwn score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta unkonwn score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bar-meta > unkonwn score meta 1`] = ` Score (1,1) -> (1,33) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - metaData: Array [ + metaData: [ Meta (1,3) -> (1,25) { tag: Tag "notExisting" (1,3) -> (1,15) { prefix: Backslash (1,3) -> (1,4), @@ -3307,17 +3085,17 @@ Score (1,1) -> (1,33) { }, arguments: Arguments (1,16) -> (1,25) { openParenthesis: LParen (1,16) -> (1,17), - arguments: Array [ + arguments: [ String "Value" (1,17) -> (1,23), ], closeParenthesis: RParen (1,24) -> (1,25), }, }, ], - beats: Array [ + beats: [ Beat (1,26) -> (1,28) { notes: NoteList (1,26) -> (1,28) { - notes: Array [ + notes: [ Note (1,26) -> (1,28) { noteValue: Ident "C4" (1,26) -> (1,28), }, @@ -3328,10 +3106,10 @@ Score (1,1) -> (1,33) { pipe: Pipe (1,29) -> (1,30), }, Bar (1,31) -> (1,33) { - beats: Array [ + beats: [ Beat (1,31) -> (1,33) { notes: NoteList (1,31) -> (1,33) { - notes: Array [ + notes: [ Note (1,31) -> (1,33) { noteValue: Ident "C4" (1,31) -> (1,33), }, @@ -3344,18 +3122,18 @@ Score (1,1) -> (1,33) { } `; -exports[`AlphaTexParserTest valid-bar-meta unkonwn score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > empty at end > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bar-meta unkonwn score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > empty at end > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars empty at end 1`] = ` +exports[`AlphaTexParserTest > valid-bars > empty at end 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,5) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3366,10 +3144,10 @@ Score (1,1) -> (1,10) { pipe: Pipe (1,4) -> (1,5), }, Bar (1,6) -> (1,10) { - beats: Array [ + beats: [ Beat (1,6) -> (1,8) { notes: NoteList (1,6) -> (1,8) { - notes: Array [ + notes: [ Note (1,6) -> (1,8) { noteValue: Ident "C4" (1,6) -> (1,8), }, @@ -3383,18 +3161,18 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParserTest valid-bars empty at end: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > multiple empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars empty at end: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > multiple empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars multiple empty 1`] = ` +exports[`AlphaTexParserTest > valid-bars > multiple empty 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,5) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3405,10 +3183,10 @@ Score (1,1) -> (1,16) { pipe: Pipe (1,4) -> (1,5), }, Bar (1,6) -> (1,10) { - beats: Array [ + beats: [ Beat (1,6) -> (1,8) { notes: NoteList (1,6) -> (1,8) { - notes: Array [ + notes: [ Note (1,6) -> (1,8) { noteValue: Ident "C4" (1,6) -> (1,8), }, @@ -3431,14 +3209,18 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest valid-bars multiple empty then filled 1`] = ` +exports[`AlphaTexParserTest > valid-bars > multiple empty then filled > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-bars > multiple empty then filled > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-bars > multiple empty then filled 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,5) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3449,10 +3231,10 @@ Score (1,1) -> (1,19) { pipe: Pipe (1,4) -> (1,5), }, Bar (1,6) -> (1,10) { - beats: Array [ + beats: [ Beat (1,6) -> (1,8) { notes: NoteList (1,6) -> (1,8) { - notes: Array [ + notes: [ Note (1,6) -> (1,8) { noteValue: Ident "C4" (1,6) -> (1,8), }, @@ -3472,10 +3254,10 @@ Score (1,1) -> (1,19) { pipe: Pipe (1,15) -> (1,16), }, Bar (1,17) -> (1,19) { - beats: Array [ + beats: [ Beat (1,17) -> (1,19) { notes: NoteList (1,17) -> (1,19) { - notes: Array [ + notes: [ Note (1,17) -> (1,19) { noteValue: Ident "C4" (1,17) -> (1,19), }, @@ -3488,22 +3270,18 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest valid-bars multiple empty then filled: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > no score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars multiple empty then filled: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > no score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars multiple empty: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-bars multiple empty: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-bars no score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bars > no score meta 1`] = ` Score (1,1) -> (1,9) { - bars: Array [ + bars: [ Bar (1,2) -> (1,6) { - beats: Array [ + beats: [ Beat (1,2) -> (1,4) { notes: NoteList (1,2) -> (1,4) { - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -3514,10 +3292,10 @@ Score (1,1) -> (1,9) { pipe: Pipe (1,5) -> (1,6), }, Bar (1,7) -> (1,9) { - beats: Array [ + beats: [ Beat (1,7) -> (1,9) { notes: NoteList (1,7) -> (1,9) { - notes: Array [ + notes: [ Note (1,7) -> (1,9) { noteValue: Ident "C4" (1,7) -> (1,9), }, @@ -3530,39 +3308,39 @@ Score (1,1) -> (1,9) { } `; -exports[`AlphaTexParserTest valid-bars no score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > with score meta > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars no score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-bars > with score meta > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars with score meta 1`] = ` +exports[`AlphaTexParserTest > valid-bars > with score meta 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,13) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,13) { - arguments: Array [ + arguments: [ String "Test" (1,8) -> (1,13) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,17) -> (1,19) { notes: NoteList (1,17) -> (1,19) { - notes: Array [ + notes: [ Note (1,17) -> (1,19) { noteValue: Ident "C4" (1,17) -> (1,19), }, @@ -3573,10 +3351,10 @@ Score (1,1) -> (1,24) { pipe: Pipe (1,20) -> (1,21), }, Bar (1,22) -> (1,24) { - beats: Array [ + beats: [ Beat (1,22) -> (1,24) { notes: NoteList (1,22) -> (1,24) { - notes: Array [ + notes: [ Note (1,22) -> (1,24) { noteValue: Ident "C4" (1,22) -> (1,24), }, @@ -3589,18 +3367,18 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest valid-bars with score meta: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-bars with score meta: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beat-effects > empty 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3610,7 +3388,7 @@ Score (1,1) -> (1,8) { durationValue: Number "8" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,8) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [], + properties: [], closeBrace: RBrace (1,7) -> (1,8), }, }, @@ -3620,18 +3398,18 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParserTest valid-beat-effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beat-effects > known 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3641,11 +3419,11 @@ Score (1,1) -> (1,14) { durationValue: Number "8" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,14) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,13) { property: Ident "tu" (1,7) -> (1,9), properties: Arguments (1,10) -> (1,13) { - arguments: Array [ + arguments: [ Number "2" (1,10) -> (1,11) { parameterIndices: Map { 0 => 0, @@ -3658,7 +3436,7 @@ Score (1,1) -> (1,14) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3674,14 +3452,18 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParserTest valid-beat-effects known with list 1`] = ` +exports[`AlphaTexParserTest > valid-beat-effects > known with list > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beat-effects > known with list > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beat-effects > known with list 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - beats: Array [ + beats: [ Beat (1,1) -> (1,19) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3691,12 +3473,12 @@ Score (1,1) -> (1,19) { durationValue: Number "8" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,19) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,18) { property: Ident "tb" (1,7) -> (1,9), properties: Arguments (1,10) -> (1,18) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Number "0" (1,11) -> (1,12), Number "-2" (1,13) -> (1,15), Number "0" (1,16) -> (1,17), @@ -3714,22 +3496,18 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest valid-beat-effects known with list: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > multiple > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects known with list: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > multiple > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects known: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beat-effects known: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beat-effects multiple 1`] = ` +exports[`AlphaTexParserTest > valid-beat-effects > multiple 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - beats: Array [ + beats: [ Beat (1,1) -> (1,30) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3739,7 +3517,7 @@ Score (1,1) -> (1,30) { durationValue: Number "8" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,30) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,10) { property: Ident "cre" (1,7) -> (1,10), }, @@ -3747,7 +3525,7 @@ Score (1,1) -> (1,30) { property: Ident "tb" (1,11) -> (1,13), properties: Arguments (1,14) -> (1,22) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ Number "0" (1,15) -> (1,16), Number "-2" (1,17) -> (1,19), Number "0" (1,20) -> (1,21), @@ -3758,7 +3536,7 @@ Score (1,1) -> (1,30) { Prop (1,23) -> (1,29) { property: Ident "tu" (1,23) -> (1,25), properties: Arguments (1,26) -> (1,29) { - arguments: Array [ + arguments: [ Number "3" (1,26) -> (1,27) { parameterIndices: Map { 0 => 0, @@ -3771,7 +3549,7 @@ Score (1,1) -> (1,30) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3787,18 +3565,18 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest valid-beat-effects multiple: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > unknown > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects multiple: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beat-effects > unknown > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects unknown 1`] = ` +exports[`AlphaTexParserTest > valid-beat-effects > unknown 1`] = ` Score (1,1) -> (1,23) { - bars: Array [ + bars: [ Bar (1,1) -> (1,23) { - beats: Array [ + beats: [ Beat (1,1) -> (1,23) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -3808,12 +3586,12 @@ Score (1,1) -> (1,23) { durationValue: Number "8" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,23) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,22) { property: Ident "unknown" (1,7) -> (1,14), properties: Arguments (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Number "1" (1,16) -> (1,17), Number "2" (1,18) -> (1,19), Number "3" (1,20) -> (1,21), @@ -3831,18 +3609,18 @@ Score (1,1) -> (1,23) { } `; -exports[`AlphaTexParserTest valid-beat-effects unknown: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > basic > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beat-effects unknown: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > basic > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted basic 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > basic 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,4) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -3853,7 +3631,7 @@ Score (1,1) -> (1,8) { }, Beat (1,5) -> (1,8) { notes: NoteList (1,5) -> (1,8) { - notes: Array [ + notes: [ Note (1,5) -> (1,8) { noteValue: Number "4" (1,5) -> (1,6), noteStringDot: Dot (1,6) -> (1,7), @@ -3868,22 +3646,22 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted basic: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > complex > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted basic: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > complex > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted complex 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > complex 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - beats: Array [ + beats: [ Beat (1,1) -> (1,26) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), value: Number "2" (1,2) -> (1,3), }, notes: NoteList (1,4) -> (1,7) { - notes: Array [ + notes: [ Note (1,4) -> (1,7) { noteValue: Number "3" (1,4) -> (1,5), noteStringDot: Dot (1,5) -> (1,6), @@ -3897,14 +3675,14 @@ Score (1,1) -> (1,26) { beatMultiplierValue: Number "2" (1,12) -> (1,13), beatEffects: Props (1,14) -> (1,26) { openBrace: LBrace (1,14) -> (1,15), - properties: Array [ + properties: [ Prop (1,15) -> (1,18) { property: Ident "cre" (1,15) -> (1,18), }, Prop (1,19) -> (1,25) { property: Ident "tu" (1,19) -> (1,21), properties: Arguments (1,22) -> (1,25) { - arguments: Array [ + arguments: [ Number "3" (1,22) -> (1,23) { parameterIndices: Map { 0 => 0, @@ -3917,7 +3695,7 @@ Score (1,1) -> (1,26) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -3933,18 +3711,18 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted complex: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted complex: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted duration 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -3957,7 +3735,7 @@ Score (1,1) -> (1,12) { }, Beat (1,7) -> (1,12) { notes: NoteList (1,7) -> (1,10) { - notes: Array [ + notes: [ Note (1,7) -> (1,10) { noteValue: Number "4" (1,7) -> (1,8), noteStringDot: Dot (1,8) -> (1,9), @@ -3974,18 +3752,22 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted duration change 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration change > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration change > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-basic-fretted > duration change 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,7) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), value: Number "2" (1,2) -> (1,3), }, notes: NoteList (1,4) -> (1,7) { - notes: Array [ + notes: [ Note (1,4) -> (1,7) { noteValue: Number "3" (1,4) -> (1,5), noteStringDot: Dot (1,5) -> (1,6), @@ -4000,7 +3782,7 @@ Score (1,1) -> (1,14) { value: Number "4" (1,9) -> (1,10), }, notes: NoteList (1,11) -> (1,14) { - notes: Array [ + notes: [ Note (1,11) -> (1,14) { noteValue: Number "4" (1,11) -> (1,12), noteStringDot: Dot (1,12) -> (1,13), @@ -4015,22 +3797,18 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted duration change: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted duration change: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted duration: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-basic-fretted duration: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-basic-fretted effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects empty 1`] = ` Score (1,1) -> (1,18) { - bars: Array [ + bars: [ Bar (1,1) -> (1,18) { - beats: Array [ + beats: [ Beat (1,1) -> (1,9) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -4042,13 +3820,13 @@ Score (1,1) -> (1,18) { durationValue: Number "4" (1,5) -> (1,6), beatEffects: Props (1,7) -> (1,9) { openBrace: LBrace (1,7) -> (1,8), - properties: Array [], + properties: [], closeBrace: RBrace (1,8) -> (1,9), }, }, Beat (1,10) -> (1,18) { notes: NoteList (1,10) -> (1,13) { - notes: Array [ + notes: [ Note (1,10) -> (1,13) { noteValue: Number "4" (1,10) -> (1,11), noteStringDot: Dot (1,11) -> (1,12), @@ -4060,7 +3838,7 @@ Score (1,1) -> (1,18) { durationValue: Number "4" (1,14) -> (1,15), beatEffects: Props (1,16) -> (1,18) { openBrace: LBrace (1,16) -> (1,17), - properties: Array [], + properties: [], closeBrace: RBrace (1,17) -> (1,18), }, }, @@ -4070,18 +3848,18 @@ Score (1,1) -> (1,18) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > effects known 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -4093,7 +3871,7 @@ Score (1,1) -> (1,31) { durationValue: Number "4" (1,5) -> (1,6), beatEffects: Props (1,7) -> (1,12) { openBrace: LBrace (1,7) -> (1,8), - properties: Array [ + properties: [ Prop (1,8) -> (1,9) { property: Ident "v" (1,8) -> (1,9), }, @@ -4106,7 +3884,7 @@ Score (1,1) -> (1,31) { }, Beat (1,13) -> (1,31) { notes: NoteList (1,13) -> (1,16) { - notes: Array [ + notes: [ Note (1,13) -> (1,16) { noteValue: Number "4" (1,13) -> (1,14), noteStringDot: Dot (1,14) -> (1,15), @@ -4118,14 +3896,14 @@ Score (1,1) -> (1,31) { durationValue: Number "4" (1,17) -> (1,18), beatEffects: Props (1,19) -> (1,31) { openBrace: LBrace (1,19) -> (1,20), - properties: Array [ + properties: [ Prop (1,20) -> (1,23) { property: Ident "cre" (1,20) -> (1,23), }, Prop (1,24) -> (1,30) { property: Ident "tu" (1,24) -> (1,26), properties: Arguments (1,27) -> (1,30) { - arguments: Array [ + arguments: [ Number "3" (1,27) -> (1,28) { parameterIndices: Map { 0 => 0, @@ -4138,7 +3916,7 @@ Score (1,1) -> (1,31) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4154,18 +3932,18 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted effects known: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > multiplier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted effects known: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > multiplier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted multiplier 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > multiplier 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -4178,7 +3956,7 @@ Score (1,1) -> (1,12) { }, Beat (1,7) -> (1,12) { notes: NoteList (1,7) -> (1,10) { - notes: Array [ + notes: [ Note (1,7) -> (1,10) { noteValue: Number "4" (1,7) -> (1,8), noteStringDot: Dot (1,8) -> (1,9), @@ -4195,18 +3973,18 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted multiplier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > spacing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted multiplier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-fretted > spacing > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted spacing 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-fretted > spacing 1`] = ` Score (1,1) -> (1,37) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { notes: NoteList (1,1) -> (1,4) { - notes: Array [ + notes: [ Note (1,1) -> (1,4) { noteValue: Number "3" (1,1) -> (1,2), noteStringDot: Dot (1,2) -> (1,3), @@ -4221,10 +3999,10 @@ Score (1,1) -> (1,37) { pipe: Pipe (1,7) -> (1,8), }, Bar (1,9) -> (1,20) { - beats: Array [ + beats: [ Beat (1,9) -> (1,18) { notes: NoteList (1,9) -> (1,14) { - notes: Array [ + notes: [ Note (1,9) -> (1,14) { noteValue: Number "3" (1,9) -> (1,10), noteStringDot: Dot (1,11) -> (1,12), @@ -4239,10 +4017,10 @@ Score (1,1) -> (1,37) { pipe: Pipe (1,19) -> (1,20), }, Bar (1,21) -> (1,29) { - beats: Array [ + beats: [ Beat (1,21) -> (1,27) { notes: NoteList (1,21) -> (1,24) { - notes: Array [ + notes: [ Note (1,21) -> (1,24) { noteValue: Number "3" (1,21) -> (1,22), noteStringDot: Dot (1,22) -> (1,23), @@ -4257,10 +4035,10 @@ Score (1,1) -> (1,37) { pipe: Pipe (1,28) -> (1,29), }, Bar (1,30) -> (1,37) { - beats: Array [ + beats: [ Beat (1,30) -> (1,37) { notes: NoteList (1,30) -> (1,35) { - notes: Array [ + notes: [ Note (1,30) -> (1,35) { noteValue: Number "3" (1,30) -> (1,31), noteStringDot: Dot (1,32) -> (1,33), @@ -4277,18 +4055,18 @@ Score (1,1) -> (1,37) { } `; -exports[`AlphaTexParserTest valid-beats-basic-fretted spacing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > basic > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-fretted spacing: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > basic > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched basic 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > basic 1`] = ` Score (1,1) -> (1,6) { - bars: Array [ + bars: [ Bar (1,1) -> (1,6) { - beats: Array [ + beats: [ Beat (1,1) -> (1,3) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -4297,7 +4075,7 @@ Score (1,1) -> (1,6) { }, Beat (1,4) -> (1,6) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C5" (1,4) -> (1,6), }, @@ -4310,22 +4088,22 @@ Score (1,1) -> (1,6) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched basic: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > complex > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched basic: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > complex > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched complex 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > complex 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - beats: Array [ + beats: [ Beat (1,1) -> (1,25) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), value: Number "2" (1,2) -> (1,3), }, notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -4337,14 +4115,14 @@ Score (1,1) -> (1,25) { beatMultiplierValue: Number "2" (1,11) -> (1,12), beatEffects: Props (1,13) -> (1,25) { openBrace: LBrace (1,13) -> (1,14), - properties: Array [ + properties: [ Prop (1,14) -> (1,17) { property: Ident "cre" (1,14) -> (1,17), }, Prop (1,18) -> (1,24) { property: Ident "tu" (1,18) -> (1,20), properties: Arguments (1,21) -> (1,24) { - arguments: Array [ + arguments: [ Number "3" (1,21) -> (1,22) { parameterIndices: Map { 0 => 0, @@ -4357,7 +4135,7 @@ Score (1,1) -> (1,25) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4373,18 +4151,18 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched complex: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched complex: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched duration 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,5) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -4395,7 +4173,7 @@ Score (1,1) -> (1,10) { }, Beat (1,6) -> (1,10) { notes: NoteList (1,6) -> (1,8) { - notes: Array [ + notes: [ Note (1,6) -> (1,8) { noteValue: Ident "C5" (1,6) -> (1,8), }, @@ -4410,18 +4188,22 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched duration change 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration change > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration change > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-basic-pitched > duration change 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), value: Number "2" (1,2) -> (1,3), }, notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -4434,7 +4216,7 @@ Score (1,1) -> (1,12) { value: Number "4" (1,8) -> (1,9), }, notes: NoteList (1,10) -> (1,12) { - notes: Array [ + notes: [ Note (1,10) -> (1,12) { noteValue: Ident "C5" (1,10) -> (1,12), }, @@ -4447,22 +4229,18 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched duration change: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched duration change: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched duration: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-basic-pitched duration: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-basic-pitched effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects empty 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -4472,13 +4250,13 @@ Score (1,1) -> (1,16) { durationValue: Number "4" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,8) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [], + properties: [], closeBrace: RBrace (1,7) -> (1,8), }, }, Beat (1,9) -> (1,16) { notes: NoteList (1,9) -> (1,11) { - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C5" (1,9) -> (1,11), }, @@ -4488,7 +4266,7 @@ Score (1,1) -> (1,16) { durationValue: Number "4" (1,12) -> (1,13), beatEffects: Props (1,14) -> (1,16) { openBrace: LBrace (1,14) -> (1,15), - properties: Array [], + properties: [], closeBrace: RBrace (1,15) -> (1,16), }, }, @@ -4498,18 +4276,18 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > effects known 1`] = ` Score (1,1) -> (1,29) { - bars: Array [ + bars: [ Bar (1,1) -> (1,29) { - beats: Array [ + beats: [ Beat (1,1) -> (1,11) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -4519,7 +4297,7 @@ Score (1,1) -> (1,29) { durationValue: Number "4" (1,4) -> (1,5), beatEffects: Props (1,6) -> (1,11) { openBrace: LBrace (1,6) -> (1,7), - properties: Array [ + properties: [ Prop (1,7) -> (1,8) { property: Ident "v" (1,7) -> (1,8), }, @@ -4532,7 +4310,7 @@ Score (1,1) -> (1,29) { }, Beat (1,12) -> (1,29) { notes: NoteList (1,12) -> (1,14) { - notes: Array [ + notes: [ Note (1,12) -> (1,14) { noteValue: Ident "C5" (1,12) -> (1,14), }, @@ -4542,14 +4320,14 @@ Score (1,1) -> (1,29) { durationValue: Number "4" (1,15) -> (1,16), beatEffects: Props (1,17) -> (1,29) { openBrace: LBrace (1,17) -> (1,18), - properties: Array [ + properties: [ Prop (1,18) -> (1,21) { property: Ident "cre" (1,18) -> (1,21), }, Prop (1,22) -> (1,28) { property: Ident "tu" (1,22) -> (1,24), properties: Arguments (1,25) -> (1,28) { - arguments: Array [ + arguments: [ Number "3" (1,25) -> (1,26) { parameterIndices: Map { 0 => 0, @@ -4562,7 +4340,7 @@ Score (1,1) -> (1,29) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4578,18 +4356,18 @@ Score (1,1) -> (1,29) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched effects known: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > multiplier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched effects known: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-basic-pitched > multiplier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched multiplier 1`] = ` +exports[`AlphaTexParserTest > valid-beats-basic-pitched > multiplier 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,5) { notes: NoteList (1,1) -> (1,3) { - notes: Array [ + notes: [ Note (1,1) -> (1,3) { noteValue: Ident "C4" (1,1) -> (1,3), }, @@ -4600,7 +4378,7 @@ Score (1,1) -> (1,10) { }, Beat (1,6) -> (1,10) { notes: NoteList (1,6) -> (1,8) { - notes: Array [ + notes: [ Note (1,6) -> (1,8) { noteValue: Ident "C5" (1,6) -> (1,8), }, @@ -4615,19 +4393,19 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParserTest valid-beats-basic-pitched multiplier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > chord > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-basic-pitched multiplier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > chord > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted chord 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > chord 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,10) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -4645,7 +4423,7 @@ Score (1,1) -> (1,20) { Beat (1,11) -> (1,20) { notes: NoteList (1,11) -> (1,20) { openParenthesis: LParen (1,11) -> (1,12), - notes: Array [ + notes: [ Note (1,12) -> (1,15) { noteValue: Number "1" (1,12) -> (1,13), noteStringDot: Dot (1,13) -> (1,14), @@ -4666,15 +4444,15 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted chord: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > complex > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted chord: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > complex > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted complex 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > complex 1`] = ` Score (1,1) -> (1,32) { - bars: Array [ + bars: [ Bar (1,1) -> (1,32) { - beats: Array [ + beats: [ Beat (1,1) -> (1,32) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -4682,7 +4460,7 @@ Score (1,1) -> (1,32) { }, notes: NoteList (1,4) -> (1,13) { openParenthesis: LParen (1,4) -> (1,5), - notes: Array [ + notes: [ Note (1,5) -> (1,8) { noteValue: Number "3" (1,5) -> (1,6), noteStringDot: Dot (1,6) -> (1,7), @@ -4702,14 +4480,14 @@ Score (1,1) -> (1,32) { beatMultiplierValue: Number "2" (1,18) -> (1,19), beatEffects: Props (1,20) -> (1,32) { openBrace: LBrace (1,20) -> (1,21), - properties: Array [ + properties: [ Prop (1,21) -> (1,24) { property: Ident "cre" (1,21) -> (1,24), }, Prop (1,25) -> (1,31) { property: Ident "tu" (1,25) -> (1,27), properties: Arguments (1,28) -> (1,31) { - arguments: Array [ + arguments: [ Number "3" (1,28) -> (1,29) { parameterIndices: Map { 0 => 0, @@ -4722,7 +4500,7 @@ Score (1,1) -> (1,32) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -4738,19 +4516,19 @@ Score (1,1) -> (1,32) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted complex: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted complex: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted duration 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,10) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -4770,7 +4548,7 @@ Score (1,1) -> (1,24) { Beat (1,13) -> (1,24) { notes: NoteList (1,13) -> (1,22) { openParenthesis: LParen (1,13) -> (1,14), - notes: Array [ + notes: [ Note (1,14) -> (1,17) { noteValue: Number "1" (1,14) -> (1,15), noteStringDot: Dot (1,15) -> (1,16), @@ -4793,11 +4571,15 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted duration change 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration change > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration change > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-chord-fretted > duration change 1`] = ` Score (1,1) -> (1,26) { - bars: Array [ + bars: [ Bar (1,1) -> (1,26) { - beats: Array [ + beats: [ Beat (1,1) -> (1,13) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -4805,7 +4587,7 @@ Score (1,1) -> (1,26) { }, notes: NoteList (1,4) -> (1,13) { openParenthesis: LParen (1,4) -> (1,5), - notes: Array [ + notes: [ Note (1,5) -> (1,8) { noteValue: Number "3" (1,5) -> (1,6), noteStringDot: Dot (1,6) -> (1,7), @@ -4827,7 +4609,7 @@ Score (1,1) -> (1,26) { }, notes: NoteList (1,17) -> (1,26) { openParenthesis: LParen (1,17) -> (1,18), - notes: Array [ + notes: [ Note (1,18) -> (1,21) { noteValue: Number "1" (1,18) -> (1,19), noteStringDot: Dot (1,19) -> (1,20), @@ -4848,23 +4630,19 @@ Score (1,1) -> (1,26) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted duration change: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted duration change: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted duration: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-chord-fretted duration: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-chord-fretted effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects empty 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - beats: Array [ + beats: [ Beat (1,1) -> (1,15) { notes: NoteList (1,1) -> (1,10) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -4882,14 +4660,14 @@ Score (1,1) -> (1,30) { durationValue: Number "4" (1,11) -> (1,12), beatEffects: Props (1,13) -> (1,15) { openBrace: LBrace (1,13) -> (1,14), - properties: Array [], + properties: [], closeBrace: RBrace (1,14) -> (1,15), }, }, Beat (1,16) -> (1,30) { notes: NoteList (1,16) -> (1,25) { openParenthesis: LParen (1,16) -> (1,17), - notes: Array [ + notes: [ Note (1,17) -> (1,20) { noteValue: Number "1" (1,17) -> (1,18), noteStringDot: Dot (1,18) -> (1,19), @@ -4907,7 +4685,7 @@ Score (1,1) -> (1,30) { durationValue: Number "4" (1,26) -> (1,27), beatEffects: Props (1,28) -> (1,30) { openBrace: LBrace (1,28) -> (1,29), - properties: Array [], + properties: [], closeBrace: RBrace (1,29) -> (1,30), }, }, @@ -4917,19 +4695,19 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > effects known 1`] = ` Score (1,1) -> (1,43) { - bars: Array [ + bars: [ Bar (1,1) -> (1,43) { - beats: Array [ + beats: [ Beat (1,1) -> (1,18) { notes: NoteList (1,1) -> (1,10) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -4947,7 +4725,7 @@ Score (1,1) -> (1,43) { durationValue: Number "4" (1,11) -> (1,12), beatEffects: Props (1,13) -> (1,18) { openBrace: LBrace (1,13) -> (1,14), - properties: Array [ + properties: [ Prop (1,14) -> (1,15) { property: Ident "v" (1,14) -> (1,15), }, @@ -4961,7 +4739,7 @@ Score (1,1) -> (1,43) { Beat (1,19) -> (1,43) { notes: NoteList (1,19) -> (1,28) { openParenthesis: LParen (1,19) -> (1,20), - notes: Array [ + notes: [ Note (1,20) -> (1,23) { noteValue: Number "1" (1,20) -> (1,21), noteStringDot: Dot (1,21) -> (1,22), @@ -4979,14 +4757,14 @@ Score (1,1) -> (1,43) { durationValue: Number "4" (1,29) -> (1,30), beatEffects: Props (1,31) -> (1,43) { openBrace: LBrace (1,31) -> (1,32), - properties: Array [ + properties: [ Prop (1,32) -> (1,35) { property: Ident "cre" (1,32) -> (1,35), }, Prop (1,36) -> (1,42) { property: Ident "tu" (1,36) -> (1,38), properties: Arguments (1,39) -> (1,42) { - arguments: Array [ + arguments: [ Number "3" (1,39) -> (1,40) { parameterIndices: Map { 0 => 0, @@ -4999,7 +4777,7 @@ Score (1,1) -> (1,43) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5015,19 +4793,19 @@ Score (1,1) -> (1,43) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted effects known: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > multiplier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted effects known: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > multiplier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted multiplier 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > multiplier 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,10) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,5) { noteValue: Number "3" (1,2) -> (1,3), noteStringDot: Dot (1,3) -> (1,4), @@ -5047,7 +4825,7 @@ Score (1,1) -> (1,24) { Beat (1,13) -> (1,24) { notes: NoteList (1,13) -> (1,22) { openParenthesis: LParen (1,13) -> (1,14), - notes: Array [ + notes: [ Note (1,14) -> (1,17) { noteValue: Number "1" (1,14) -> (1,15), noteStringDot: Dot (1,15) -> (1,16), @@ -5070,19 +4848,19 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted multiplier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > spacing > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted multiplier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-fretted > spacing > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted spacing 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-fretted > spacing 1`] = ` Score (1,1) -> (5,18) { - bars: Array [ + bars: [ Bar (2,13) -> (5,18) { - beats: Array [ + beats: [ Beat (2,13) -> (5,18) { notes: NoteList (2,13) -> (5,14) { openParenthesis: LParen (2,13) -> (2,14), - notes: Array [ + notes: [ Note (3,17) -> (3,20) { noteValue: Number "3" (3,17) -> (3,18), noteStringDot: Dot (3,18) -> (3,19), @@ -5105,19 +4883,19 @@ Score (1,1) -> (5,18) { } `; -exports[`AlphaTexParserTest valid-beats-chord-fretted spacing: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > chord > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-fretted spacing: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > chord > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched chord 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > chord 1`] = ` Score (1,1) -> (1,16) { - bars: Array [ + bars: [ Bar (1,1) -> (1,16) { - beats: Array [ + beats: [ Beat (1,1) -> (1,8) { notes: NoteList (1,1) -> (1,8) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -5131,7 +4909,7 @@ Score (1,1) -> (1,16) { Beat (1,9) -> (1,16) { notes: NoteList (1,9) -> (1,16) { openParenthesis: LParen (1,9) -> (1,10), - notes: Array [ + notes: [ Note (1,10) -> (1,12) { noteValue: Ident "D4" (1,10) -> (1,12), }, @@ -5148,15 +4926,15 @@ Score (1,1) -> (1,16) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched chord: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > complex > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched chord: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > complex > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched complex 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > complex 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - beats: Array [ + beats: [ Beat (1,1) -> (1,30) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -5164,7 +4942,7 @@ Score (1,1) -> (1,30) { }, notes: NoteList (1,4) -> (1,11) { openParenthesis: LParen (1,4) -> (1,5), - notes: Array [ + notes: [ Note (1,5) -> (1,7) { noteValue: Ident "C4" (1,5) -> (1,7), }, @@ -5180,14 +4958,14 @@ Score (1,1) -> (1,30) { beatMultiplierValue: Number "2" (1,16) -> (1,17), beatEffects: Props (1,18) -> (1,30) { openBrace: LBrace (1,18) -> (1,19), - properties: Array [ + properties: [ Prop (1,19) -> (1,22) { property: Ident "cre" (1,19) -> (1,22), }, Prop (1,23) -> (1,29) { property: Ident "tu" (1,23) -> (1,25), properties: Arguments (1,26) -> (1,29) { - arguments: Array [ + arguments: [ Number "3" (1,26) -> (1,27) { parameterIndices: Map { 0 => 0, @@ -5200,7 +4978,7 @@ Score (1,1) -> (1,30) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5216,19 +4994,19 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched complex: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched complex: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched duration 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,8) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -5244,7 +5022,7 @@ Score (1,1) -> (1,20) { Beat (1,11) -> (1,20) { notes: NoteList (1,11) -> (1,18) { openParenthesis: LParen (1,11) -> (1,12), - notes: Array [ + notes: [ Note (1,12) -> (1,14) { noteValue: Ident "D4" (1,12) -> (1,14), }, @@ -5263,11 +5041,15 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched duration change 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration change > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration change > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-chord-pitched > duration change 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,1) -> (1,22) { - beats: Array [ + beats: [ Beat (1,1) -> (1,11) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -5275,7 +5057,7 @@ Score (1,1) -> (1,22) { }, notes: NoteList (1,4) -> (1,11) { openParenthesis: LParen (1,4) -> (1,5), - notes: Array [ + notes: [ Note (1,5) -> (1,7) { noteValue: Ident "C4" (1,5) -> (1,7), }, @@ -5293,7 +5075,7 @@ Score (1,1) -> (1,22) { }, notes: NoteList (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - notes: Array [ + notes: [ Note (1,16) -> (1,18) { noteValue: Ident "D4" (1,16) -> (1,18), }, @@ -5310,23 +5092,19 @@ Score (1,1) -> (1,22) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched duration change: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched duration change: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched duration: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-chord-pitched duration: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-chord-pitched effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects empty 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,1) -> (1,22) { - beats: Array [ + beats: [ Beat (1,1) -> (1,11) { notes: NoteList (1,1) -> (1,8) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -5338,14 +5116,14 @@ Score (1,1) -> (1,22) { }, beatEffects: Props (1,9) -> (1,11) { openBrace: LBrace (1,9) -> (1,10), - properties: Array [], + properties: [], closeBrace: RBrace (1,10) -> (1,11), }, }, Beat (1,12) -> (1,22) { notes: NoteList (1,12) -> (1,19) { openParenthesis: LParen (1,12) -> (1,13), - notes: Array [ + notes: [ Note (1,13) -> (1,15) { noteValue: Ident "D4" (1,13) -> (1,15), }, @@ -5357,7 +5135,7 @@ Score (1,1) -> (1,22) { }, beatEffects: Props (1,20) -> (1,22) { openBrace: LBrace (1,20) -> (1,21), - properties: Array [], + properties: [], closeBrace: RBrace (1,21) -> (1,22), }, }, @@ -5367,19 +5145,19 @@ Score (1,1) -> (1,22) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > effects known 1`] = ` Score (1,1) -> (1,35) { - bars: Array [ + bars: [ Bar (1,1) -> (1,35) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,8) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -5391,7 +5169,7 @@ Score (1,1) -> (1,35) { }, beatEffects: Props (1,9) -> (1,14) { openBrace: LBrace (1,9) -> (1,10), - properties: Array [ + properties: [ Prop (1,10) -> (1,11) { property: Ident "v" (1,10) -> (1,11), }, @@ -5405,7 +5183,7 @@ Score (1,1) -> (1,35) { Beat (1,15) -> (1,35) { notes: NoteList (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - notes: Array [ + notes: [ Note (1,16) -> (1,18) { noteValue: Ident "D4" (1,16) -> (1,18), }, @@ -5417,14 +5195,14 @@ Score (1,1) -> (1,35) { }, beatEffects: Props (1,23) -> (1,35) { openBrace: LBrace (1,23) -> (1,24), - properties: Array [ + properties: [ Prop (1,24) -> (1,27) { property: Ident "cre" (1,24) -> (1,27), }, Prop (1,28) -> (1,34) { property: Ident "tu" (1,28) -> (1,30), properties: Arguments (1,31) -> (1,34) { - arguments: Array [ + arguments: [ Number "3" (1,31) -> (1,32) { parameterIndices: Map { 0 => 0, @@ -5437,7 +5215,7 @@ Score (1,1) -> (1,35) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5453,19 +5231,19 @@ Score (1,1) -> (1,35) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched effects known: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > multiplier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched effects known: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-chord-pitched > multiplier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched multiplier 1`] = ` +exports[`AlphaTexParserTest > valid-beats-chord-pitched > multiplier 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { notes: NoteList (1,1) -> (1,8) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,4) { noteValue: Ident "C4" (1,2) -> (1,4), }, @@ -5481,7 +5259,7 @@ Score (1,1) -> (1,20) { Beat (1,11) -> (1,20) { notes: NoteList (1,11) -> (1,18) { openParenthesis: LParen (1,11) -> (1,12), - notes: Array [ + notes: [ Note (1,12) -> (1,14) { noteValue: Ident "D4" (1,12) -> (1,14), }, @@ -5500,15 +5278,15 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-beats-chord-pitched multiplier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > complex > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-chord-pitched multiplier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > complex > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest complex 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > complex 1`] = ` Score (1,1) -> (1,24) { - bars: Array [ + bars: [ Bar (1,1) -> (1,24) { - beats: Array [ + beats: [ Beat (1,1) -> (1,24) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -5521,14 +5299,14 @@ Score (1,1) -> (1,24) { beatMultiplierValue: Number "2" (1,10) -> (1,11), beatEffects: Props (1,12) -> (1,24) { openBrace: LBrace (1,12) -> (1,13), - properties: Array [ + properties: [ Prop (1,13) -> (1,16) { property: Ident "cre" (1,13) -> (1,16), }, Prop (1,17) -> (1,23) { property: Ident "tu" (1,17) -> (1,19), properties: Arguments (1,20) -> (1,23) { - arguments: Array [ + arguments: [ Number "3" (1,20) -> (1,21) { parameterIndices: Map { 0 => 0, @@ -5541,7 +5319,7 @@ Score (1,1) -> (1,24) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5557,15 +5335,15 @@ Score (1,1) -> (1,24) { } `; -exports[`AlphaTexParserTest valid-beats-rest complex: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > duration > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest complex: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > duration > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest duration 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > duration 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,4) { rest: Ident "r" (1,1) -> (1,2), durationDot: Dot (1,2) -> (1,3), @@ -5582,11 +5360,15 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParserTest valid-beats-rest duration change 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > duration change > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-rest > duration change > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-beats-rest > duration change 1`] = ` Score (1,1) -> (1,10) { - bars: Array [ + bars: [ Bar (1,1) -> (1,10) { - beats: Array [ + beats: [ Beat (1,1) -> (1,5) { durationChange: Duration (1,1) -> (1,3) { colon: Colon (1,1) -> (1,2), @@ -5607,26 +5389,22 @@ Score (1,1) -> (1,10) { } `; -exports[`AlphaTexParserTest valid-beats-rest duration change: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > effects empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest duration change: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > effects empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest duration: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-rest duration: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-beats-rest effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > effects empty 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,7) { rest: Ident "r" (1,1) -> (1,2), durationDot: Dot (1,2) -> (1,3), durationValue: Number "4" (1,3) -> (1,4), beatEffects: Props (1,5) -> (1,7) { openBrace: LBrace (1,5) -> (1,6), - properties: Array [], + properties: [], closeBrace: RBrace (1,6) -> (1,7), }, }, @@ -5636,7 +5414,7 @@ Score (1,1) -> (1,14) { durationValue: Number "4" (1,10) -> (1,11), beatEffects: Props (1,12) -> (1,14) { openBrace: LBrace (1,12) -> (1,13), - properties: Array [], + properties: [], closeBrace: RBrace (1,13) -> (1,14), }, }, @@ -5646,22 +5424,22 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParserTest valid-beats-rest effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > effects known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > effects known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest effects known 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > effects known 1`] = ` Score (1,1) -> (1,27) { - bars: Array [ + bars: [ Bar (1,1) -> (1,27) { - beats: Array [ + beats: [ Beat (1,1) -> (1,10) { rest: Ident "r" (1,1) -> (1,2), durationDot: Dot (1,2) -> (1,3), durationValue: Number "4" (1,3) -> (1,4), beatEffects: Props (1,5) -> (1,10) { openBrace: LBrace (1,5) -> (1,6), - properties: Array [ + properties: [ Prop (1,6) -> (1,7) { property: Ident "v" (1,6) -> (1,7), }, @@ -5678,14 +5456,14 @@ Score (1,1) -> (1,27) { durationValue: Number "4" (1,13) -> (1,14), beatEffects: Props (1,15) -> (1,27) { openBrace: LBrace (1,15) -> (1,16), - properties: Array [ + properties: [ Prop (1,16) -> (1,19) { property: Ident "cre" (1,16) -> (1,19), }, Prop (1,20) -> (1,26) { property: Ident "tu" (1,20) -> (1,22), properties: Arguments (1,23) -> (1,26) { - arguments: Array [ + arguments: [ Number "3" (1,23) -> (1,24) { parameterIndices: Map { 0 => 0, @@ -5698,7 +5476,7 @@ Score (1,1) -> (1,27) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5714,15 +5492,15 @@ Score (1,1) -> (1,27) { } `; -exports[`AlphaTexParserTest valid-beats-rest effects known: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > multiplier > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest effects known: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > multiplier > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest multiplier 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > multiplier 1`] = ` Score (1,1) -> (1,8) { - bars: Array [ + bars: [ Bar (1,1) -> (1,8) { - beats: Array [ + beats: [ Beat (1,1) -> (1,4) { rest: Ident "r" (1,1) -> (1,2), beatMultiplier: Asterisk (1,2) -> (1,3), @@ -5739,15 +5517,15 @@ Score (1,1) -> (1,8) { } `; -exports[`AlphaTexParserTest valid-beats-rest multiplier: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > rest > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest multiplier: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-beats-rest > rest > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest rest 1`] = ` +exports[`AlphaTexParserTest > valid-beats-rest > rest 1`] = ` Score (1,1) -> (1,2) { - bars: Array [ + bars: [ Bar (1,1) -> (1,2) { - beats: Array [ + beats: [ Beat (1,1) -> (1,2) { rest: Ident "r" (1,1) -> (1,2), }, @@ -5757,33 +5535,33 @@ Score (1,1) -> (1,2) { } `; -exports[`AlphaTexParserTest valid-beats-rest rest: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-empty > empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-beats-rest rest: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-empty > empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-empty empty 1`] = `Score (1,1) -> (1,1)`; +exports[`AlphaTexParserTest > valid-empty > empty 1`] = `Score (1,1) -> (1,1)`; -exports[`AlphaTexParserTest valid-empty empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > beat effects in note effect > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-empty empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > beat effects in note effect > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects beat effects in note effect 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > beat effects in note effect 1`] = ` Score (1,1) -> (1,14) { - bars: Array [ + bars: [ Bar (1,1) -> (1,14) { - beats: Array [ + beats: [ Beat (1,1) -> (1,14) { notes: NoteList (1,1) -> (1,14) { - notes: Array [ + notes: [ Note (1,1) -> (1,14) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,14) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,6) -> (1,12) { property: Ident "tu" (1,6) -> (1,8), properties: Arguments (1,9) -> (1,12) { - arguments: Array [ + arguments: [ Number "3" (1,9) -> (1,10) { parameterIndices: Map { 0 => 0, @@ -5796,7 +5574,7 @@ Score (1,1) -> (1,14) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -5815,23 +5593,23 @@ Score (1,1) -> (1,14) { } `; -exports[`AlphaTexParserTest valid-note-effects beat effects in note effect: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects beat effects in note effect: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects empty 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > empty 1`] = ` Score (1,1) -> (1,6) { - bars: Array [ + bars: [ Bar (1,1) -> (1,6) { - beats: Array [ + beats: [ Beat (1,1) -> (1,6) { notes: NoteList (1,1) -> (1,6) { - notes: Array [ + notes: [ Note (1,1) -> (1,6) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,6) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [], + properties: [], closeBrace: RBrace (1,5) -> (1,6), }, }, @@ -5844,27 +5622,27 @@ Score (1,1) -> (1,6) { } `; -exports[`AlphaTexParserTest valid-note-effects empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > known > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > known > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects known 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > known 1`] = ` Score (1,1) -> (1,12) { - bars: Array [ + bars: [ Bar (1,1) -> (1,12) { - beats: Array [ + beats: [ Beat (1,1) -> (1,12) { notes: NoteList (1,1) -> (1,12) { - notes: Array [ + notes: [ Note (1,1) -> (1,12) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,12) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,11) { property: Ident "tr" (1,5) -> (1,7), properties: Arguments (1,8) -> (1,11) { - arguments: Array [ + arguments: [ Number "4" (1,8) -> (1,9) { parameterIndices: Map { 0 => 0, @@ -5876,7 +5654,7 @@ Score (1,1) -> (1,12) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -5895,24 +5673,28 @@ Score (1,1) -> (1,12) { } `; -exports[`AlphaTexParserTest valid-note-effects known with list 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > known with list > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > known with list > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > known with list 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,1) -> (1,15) { - beats: Array [ + beats: [ Beat (1,1) -> (1,15) { notes: NoteList (1,1) -> (1,15) { - notes: Array [ + notes: [ Note (1,1) -> (1,15) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,15) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,14) { property: Ident "b" (1,5) -> (1,6), properties: Arguments (1,7) -> (1,14) { openParenthesis: LParen (1,7) -> (1,8), - arguments: Array [ + arguments: [ Number "0" (1,8) -> (1,9), Number "4" (1,10) -> (1,11), Number "0" (1,12) -> (1,13), @@ -5933,27 +5715,23 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParserTest valid-note-effects known with list: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > multiple > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects known with list: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > multiple > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects known: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects known: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects multiple 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > multiple 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,1) -> (1,20) { - beats: Array [ + beats: [ Beat (1,1) -> (1,20) { notes: NoteList (1,1) -> (1,20) { - notes: Array [ + notes: [ Note (1,1) -> (1,20) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,20) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,7) { property: Ident "nh" (1,5) -> (1,7), }, @@ -5961,7 +5739,7 @@ Score (1,1) -> (1,20) { property: Ident "b" (1,8) -> (1,9), properties: Arguments (1,10) -> (1,17) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Number "0" (1,11) -> (1,12), Number "4" (1,13) -> (1,14), Number "0" (1,15) -> (1,16), @@ -5985,20 +5763,24 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-note-effects multiple chord 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > multiple chord > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > multiple chord > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > multiple chord 1`] = ` Score (1,1) -> (1,48) { - bars: Array [ + bars: [ Bar (1,1) -> (1,48) { - beats: Array [ + beats: [ Beat (1,1) -> (1,48) { notes: NoteList (1,1) -> (1,48) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,21) { noteValue: Ident "C4" (1,2) -> (1,4), noteEffects: Props (1,5) -> (1,21) { openBrace: LBrace (1,5) -> (1,6), - properties: Array [ + properties: [ Prop (1,6) -> (1,8) { property: Ident "nh" (1,6) -> (1,8), }, @@ -6006,7 +5788,7 @@ Score (1,1) -> (1,48) { property: Ident "b" (1,9) -> (1,10), properties: Arguments (1,11) -> (1,18) { openParenthesis: LParen (1,11) -> (1,12), - arguments: Array [ + arguments: [ Number "0" (1,12) -> (1,13), Number "4" (1,14) -> (1,15), Number "0" (1,16) -> (1,17), @@ -6025,7 +5807,7 @@ Score (1,1) -> (1,48) { noteValue: Ident "C5" (1,22) -> (1,24), noteEffects: Props (1,25) -> (1,47) { openBrace: LBrace (1,25) -> (1,26), - properties: Array [ + properties: [ Prop (1,27) -> (1,28) { property: Ident "v" (1,27) -> (1,28), }, @@ -6036,7 +5818,7 @@ Score (1,1) -> (1,48) { property: Ident "unknown" (1,31) -> (1,38), properties: Arguments (1,39) -> (1,46) { openParenthesis: LParen (1,39) -> (1,40), - arguments: Array [ + arguments: [ Number "1" (1,40) -> (1,41), Number "2" (1,42) -> (1,43), Number "3" (1,44) -> (1,45), @@ -6058,20 +5840,24 @@ Score (1,1) -> (1,48) { } `; -exports[`AlphaTexParserTest valid-note-effects multiple chord beat effects 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > multiple chord beat effects > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > multiple chord beat effects > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-note-effects > multiple chord beat effects 1`] = ` Score (1,1) -> (1,59) { - bars: Array [ + bars: [ Bar (1,1) -> (1,59) { - beats: Array [ + beats: [ Beat (1,1) -> (1,59) { notes: NoteList (1,1) -> (1,48) { openParenthesis: LParen (1,1) -> (1,2), - notes: Array [ + notes: [ Note (1,2) -> (1,21) { noteValue: Ident "C4" (1,2) -> (1,4), noteEffects: Props (1,5) -> (1,21) { openBrace: LBrace (1,5) -> (1,6), - properties: Array [ + properties: [ Prop (1,6) -> (1,8) { property: Ident "nh" (1,6) -> (1,8), }, @@ -6079,7 +5865,7 @@ Score (1,1) -> (1,59) { property: Ident "b" (1,9) -> (1,10), properties: Arguments (1,11) -> (1,18) { openParenthesis: LParen (1,11) -> (1,12), - arguments: Array [ + arguments: [ Number "0" (1,12) -> (1,13), Number "4" (1,14) -> (1,15), Number "0" (1,16) -> (1,17), @@ -6098,7 +5884,7 @@ Score (1,1) -> (1,59) { noteValue: Ident "C5" (1,22) -> (1,24), noteEffects: Props (1,25) -> (1,47) { openBrace: LBrace (1,25) -> (1,26), - properties: Array [ + properties: [ Prop (1,27) -> (1,28) { property: Ident "v" (1,27) -> (1,28), }, @@ -6109,7 +5895,7 @@ Score (1,1) -> (1,59) { property: Ident "unknown" (1,31) -> (1,38), properties: Arguments (1,39) -> (1,46) { openParenthesis: LParen (1,39) -> (1,40), - arguments: Array [ + arguments: [ Number "1" (1,40) -> (1,41), Number "2" (1,42) -> (1,43), Number "3" (1,44) -> (1,45), @@ -6126,11 +5912,11 @@ Score (1,1) -> (1,59) { }, beatEffects: Props (1,49) -> (1,59) { openBrace: LBrace (1,49) -> (1,50), - properties: Array [ + properties: [ Prop (1,51) -> (1,57) { property: Ident "tu" (1,51) -> (1,53), properties: Arguments (1,54) -> (1,57) { - arguments: Array [ + arguments: [ Number "3" (1,54) -> (1,55) { parameterIndices: Map { 0 => 0, @@ -6143,7 +5929,7 @@ Score (1,1) -> (1,59) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -6159,36 +5945,28 @@ Score (1,1) -> (1,59) { } `; -exports[`AlphaTexParserTest valid-note-effects multiple chord beat effects: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects multiple chord beat effects: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects multiple chord: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > unknown > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects multiple chord: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > unknown > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects multiple: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects multiple: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-note-effects unknown 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > unknown 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,1) -> (1,21) { - beats: Array [ + beats: [ Beat (1,1) -> (1,21) { notes: NoteList (1,1) -> (1,21) { - notes: Array [ + notes: [ Note (1,1) -> (1,21) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,21) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,20) { property: Ident "unknown" (1,5) -> (1,12), properties: Arguments (1,13) -> (1,20) { openParenthesis: LParen (1,13) -> (1,14), - arguments: Array [ + arguments: [ Number "1" (1,14) -> (1,15), Number "2" (1,16) -> (1,17), Number "3" (1,18) -> (1,19), @@ -6209,23 +5987,23 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParserTest valid-note-effects unknown: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > with beat effects > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects unknown: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-note-effects > with beat effects > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects with beat effects 1`] = ` +exports[`AlphaTexParserTest > valid-note-effects > with beat effects 1`] = ` Score (1,1) -> (1,31) { - bars: Array [ + bars: [ Bar (1,1) -> (1,31) { - beats: Array [ + beats: [ Beat (1,1) -> (1,31) { notes: NoteList (1,1) -> (1,20) { - notes: Array [ + notes: [ Note (1,1) -> (1,20) { noteValue: Ident "C4" (1,1) -> (1,3), noteEffects: Props (1,4) -> (1,20) { openBrace: LBrace (1,4) -> (1,5), - properties: Array [ + properties: [ Prop (1,5) -> (1,7) { property: Ident "nh" (1,5) -> (1,7), }, @@ -6233,7 +6011,7 @@ Score (1,1) -> (1,31) { property: Ident "b" (1,8) -> (1,9), properties: Arguments (1,10) -> (1,17) { openParenthesis: LParen (1,10) -> (1,11), - arguments: Array [ + arguments: [ Number "0" (1,11) -> (1,12), Number "4" (1,13) -> (1,14), Number "0" (1,15) -> (1,16), @@ -6252,11 +6030,11 @@ Score (1,1) -> (1,31) { }, beatEffects: Props (1,21) -> (1,31) { openBrace: LBrace (1,21) -> (1,22), - properties: Array [ + properties: [ Prop (1,23) -> (1,29) { property: Ident "tu" (1,23) -> (1,25), properties: Arguments (1,26) -> (1,29) { - arguments: Array [ + arguments: [ Number "3" (1,26) -> (1,27) { parameterIndices: Map { 0 => 0, @@ -6269,7 +6047,7 @@ Score (1,1) -> (1,31) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 1, ], validated: true, @@ -6285,41 +6063,41 @@ Score (1,1) -> (1,31) { } `; -exports[`AlphaTexParserTest valid-note-effects with beat effects: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-note-effects with beat effects: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata empty 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > empty 1`] = ` Score (1,1) -> (1,3) { - bars: Array [ + bars: [ Bar (1,2) -> (1,3), ], } `; -exports[`AlphaTexParserTest valid-score-metadata empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known multiple > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known multiple > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known multiple 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > known multiple 1`] = ` Score (1,1) -> (1,33) { - bars: Array [ + bars: [ Bar (1,1) -> (1,33) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,14) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,14) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6331,14 +6109,14 @@ Score (1,1) -> (1,33) { tag: Ident "subtitle" (1,17) -> (1,25), }, arguments: Arguments (1,26) -> (1,30) { - arguments: Array [ + arguments: [ String "Sub" (1,26) -> (1,30) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6350,47 +6128,47 @@ Score (1,1) -> (1,33) { } `; -exports[`AlphaTexParserTest valid-score-metadata known multiple: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known property > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known multiple: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known property > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known property 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > known property 1`] = ` Score (1,1) -> (1,30) { - bars: Array [ + bars: [ Bar (1,1) -> (1,30) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,28) { tag: Tag "track" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "track" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,13) { - arguments: Array [ + arguments: [ String "Name" (1,8) -> (1,13) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, properties: Props (1,15) -> (1,28) { openBrace: LBrace (1,15) -> (1,16), - properties: Array [ + properties: [ Prop (1,16) -> (1,26) { property: Ident "color" (1,16) -> (1,21), properties: Arguments (1,22) -> (1,26) { - arguments: Array [ + arguments: [ String "red" (1,22) -> (1,26) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6406,32 +6184,36 @@ Score (1,1) -> (1,30) { } `; -exports[`AlphaTexParserTest valid-score-metadata known property before value 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > known property before value > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-score-metadata > known property before value > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-score-metadata > known property before value 1`] = ` Score (1,1) -> (1,32) { - bars: Array [ + bars: [ Bar (1,1) -> (1,32) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,29) { tag: Tag "chord" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "chord" (1,2) -> (1,7), }, arguments: Arguments (1,24) -> (1,29) { - arguments: Array [ + arguments: [ String "Name" (1,24) -> (1,29) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, properties: Props (1,8) -> (1,23) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,9) -> (1,22) { property: Ident "showFingering" (1,9) -> (1,22), }, @@ -6445,26 +6227,22 @@ Score (1,1) -> (1,32) { } `; -exports[`AlphaTexParserTest valid-score-metadata known property before value: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known semantic > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known property before value: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known semantic > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known property: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-score-metadata known property: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-score-metadata known semantic 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > known semantic 1`] = ` Score (1,1) -> (1,33) { - bars: Array [ + bars: [ Bar (1,1) -> (1,33) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,31) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), tag: Ident "title" (1,2) -> (1,7), }, arguments: Arguments (1,8) -> (1,31) { - arguments: Array [ + arguments: [ String "Title" (1,8) -> (1,14) { parameterIndices: Map { 0 => 0, @@ -6481,7 +6259,7 @@ Score (1,1) -> (1,33) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6493,15 +6271,15 @@ Score (1,1) -> (1,33) { } `; -exports[`AlphaTexParserTest valid-score-metadata known semantic: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known valuelist > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known semantic: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > known valuelist > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known valuelist 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > known valuelist 1`] = ` Score (1,1) -> (1,19) { - bars: Array [ + bars: [ Bar (1,1) -> (1,19) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,17) { tag: Tag "title" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -6509,7 +6287,7 @@ Score (1,1) -> (1,19) { }, arguments: Arguments (1,8) -> (1,17) { openParenthesis: LParen (1,8) -> (1,9), - arguments: Array [ + arguments: [ String "Title" (1,9) -> (1,15), ], closeParenthesis: RParen (1,16) -> (1,17), @@ -6521,15 +6299,15 @@ Score (1,1) -> (1,19) { } `; -exports[`AlphaTexParserTest valid-score-metadata known valuelist: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > notelist after props > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata known valuelist: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > notelist after props > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata notelist after props 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > notelist after props 1`] = ` Score (1,1) -> (1,28) { - bars: Array [ + bars: [ Bar (1,1) -> (1,28) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,16) { tag: Tag "staff" (1,1) -> (1,7) { prefix: Backslash (1,1) -> (1,2), @@ -6537,7 +6315,7 @@ Score (1,1) -> (1,28) { }, properties: Props (1,8) -> (1,16) { openBrace: LBrace (1,8) -> (1,9), - properties: Array [ + properties: [ Prop (1,10) -> (1,14) { property: Ident "tabs" (1,10) -> (1,14), }, @@ -6546,11 +6324,11 @@ Score (1,1) -> (1,28) { }, }, ], - beats: Array [ + beats: [ Beat (1,17) -> (1,28) { notes: NoteList (1,17) -> (1,26) { openParenthesis: LParen (1,17) -> (1,18), - notes: Array [ + notes: [ Note (1,18) -> (1,21) { noteValue: Number "3" (1,18) -> (1,19), noteStringDot: Dot (1,19) -> (1,20), @@ -6573,15 +6351,15 @@ Score (1,1) -> (1,28) { } `; -exports[`AlphaTexParserTest valid-score-metadata notelist after props: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > unknown multiple > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata notelist after props: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > unknown multiple > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata unknown multiple 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > unknown multiple 1`] = ` Score (1,1) -> (1,43) { - bars: Array [ + bars: [ Bar (1,1) -> (1,43) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "notExisting" (1,1) -> (1,13) { prefix: Backslash (1,1) -> (1,2), @@ -6589,7 +6367,7 @@ Score (1,1) -> (1,43) { }, arguments: Arguments (1,14) -> (1,23) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ String "Value" (1,15) -> (1,21), ], closeParenthesis: RParen (1,22) -> (1,23), @@ -6602,7 +6380,7 @@ Score (1,1) -> (1,43) { }, arguments: Arguments (1,37) -> (1,41) { openParenthesis: LParen (1,37) -> (1,38), - arguments: Array [ + arguments: [ String "" (1,38) -> (1,39), ], closeParenthesis: RParen (1,40) -> (1,41), @@ -6614,15 +6392,15 @@ Score (1,1) -> (1,43) { } `; -exports[`AlphaTexParserTest valid-score-metadata unknown multiple: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > unknown valuelist > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata unknown multiple: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > unknown valuelist > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata unknown valuelist 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > unknown valuelist 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,1) -> (1,25) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,23) { tag: Tag "notExisting" (1,1) -> (1,13) { prefix: Backslash (1,1) -> (1,2), @@ -6630,7 +6408,7 @@ Score (1,1) -> (1,25) { }, arguments: Arguments (1,14) -> (1,23) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ String "Value" (1,15) -> (1,21), ], closeParenthesis: RParen (1,22) -> (1,23), @@ -6642,15 +6420,15 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParserTest valid-score-metadata unknown valuelist: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata unknown valuelist: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist empty 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist empty 1`] = ` Score (1,1) -> (1,28) { - bars: Array [ + bars: [ Bar (1,1) -> (1,28) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,26) { tag: Tag "notExisting" (1,1) -> (1,13) { prefix: Backslash (1,1) -> (1,2), @@ -6658,14 +6436,14 @@ Score (1,1) -> (1,28) { }, arguments: Arguments (1,14) -> (1,23) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ String "Value" (1,15) -> (1,21), ], closeParenthesis: RParen (1,22) -> (1,23), }, properties: Props (1,24) -> (1,26) { openBrace: LBrace (1,24) -> (1,25), - properties: Array [], + properties: [], closeBrace: RBrace (1,25) -> (1,26), }, }, @@ -6675,15 +6453,15 @@ Score (1,1) -> (1,28) { } `; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist unknown prop > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist unknown prop > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist unknown prop 1`] = ` +exports[`AlphaTexParserTest > valid-score-metadata > valuelist propertylist unknown prop 1`] = ` Score (1,1) -> (1,43) { - bars: Array [ + bars: [ Bar (1,1) -> (1,43) { - metaData: Array [ + metaData: [ Meta (1,1) -> (1,41) { tag: Tag "notExisting" (1,1) -> (1,13) { prefix: Backslash (1,1) -> (1,2), @@ -6691,19 +6469,19 @@ Score (1,1) -> (1,43) { }, arguments: Arguments (1,14) -> (1,23) { openParenthesis: LParen (1,14) -> (1,15), - arguments: Array [ + arguments: [ String "Value" (1,15) -> (1,21), ], closeParenthesis: RParen (1,22) -> (1,23), }, properties: Props (1,24) -> (1,41) { openBrace: LBrace (1,24) -> (1,25), - properties: Array [ + properties: [ Prop (1,25) -> (1,40) { property: Ident "unknown" (1,25) -> (1,32), properties: Arguments (1,33) -> (1,40) { openParenthesis: LParen (1,33) -> (1,34), - arguments: Array [ + arguments: [ Number "1" (1,34) -> (1,35), Number "2" (1,36) -> (1,37), Number "3" (1,38) -> (1,39), @@ -6721,18 +6499,18 @@ Score (1,1) -> (1,43) { } `; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist unknown prop: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic fretted > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-score-metadata valuelist propertylist unknown prop: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic fretted > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic fretted 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > basic fretted 1`] = ` Score (1,1) -> (1,21) { - bars: Array [ + bars: [ Bar (1,2) -> (1,9) { - beats: Array [ + beats: [ Beat (1,4) -> (1,9) { notes: NoteList (1,4) -> (1,7) { - notes: Array [ + notes: [ Note (1,4) -> (1,7) { noteValue: Number "3" (1,4) -> (1,5), noteStringDot: Dot (1,5) -> (1,6), @@ -6744,14 +6522,14 @@ Score (1,1) -> (1,21) { ], }, Bar (1,10) -> (1,21) { - metaData: Array [ + metaData: [ Meta (1,10) -> (1,21) { tag: Tag "sync" (1,10) -> (1,15) { prefix: Backslash (1,10) -> (1,11), tag: Ident "sync" (1,11) -> (1,15), }, arguments: Arguments (1,16) -> (1,21) { - arguments: Array [ + arguments: [ Number "1" (1,16) -> (1,17) { parameterIndices: Map { 0 => 0, @@ -6768,7 +6546,7 @@ Score (1,1) -> (1,21) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6780,18 +6558,18 @@ Score (1,1) -> (1,21) { } `; -exports[`AlphaTexParserTest valid-sync-points basic fretted: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic simple numbered > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic fretted: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic simple numbered > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic simple numbered 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > basic simple numbered 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,8) { notes: NoteList (1,4) -> (1,8) { - notes: Array [ + notes: [ Note (1,4) -> (1,8) { noteValue: Number "32" (1,4) -> (1,6), }, @@ -6801,14 +6579,14 @@ Score (1,1) -> (1,20) { ], }, Bar (1,9) -> (1,20) { - metaData: Array [ + metaData: [ Meta (1,9) -> (1,20) { tag: Tag "sync" (1,9) -> (1,14) { prefix: Backslash (1,9) -> (1,10), tag: Ident "sync" (1,10) -> (1,14), }, arguments: Arguments (1,15) -> (1,20) { - arguments: Array [ + arguments: [ Number "1" (1,15) -> (1,16) { parameterIndices: Map { 0 => 0, @@ -6825,7 +6603,7 @@ Score (1,1) -> (1,20) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6837,18 +6615,18 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-sync-points basic simple numbered: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic simple pitched > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic simple numbered: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > basic simple pitched > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic simple pitched 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > basic simple pitched 1`] = ` Score (1,1) -> (1,20) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,8) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -6858,14 +6636,14 @@ Score (1,1) -> (1,20) { ], }, Bar (1,9) -> (1,20) { - metaData: Array [ + metaData: [ Meta (1,9) -> (1,20) { tag: Tag "sync" (1,9) -> (1,14) { prefix: Backslash (1,9) -> (1,10), tag: Ident "sync" (1,10) -> (1,14), }, arguments: Arguments (1,15) -> (1,20) { - arguments: Array [ + arguments: [ Number "1" (1,15) -> (1,16) { parameterIndices: Map { 0 => 0, @@ -6882,7 +6660,7 @@ Score (1,1) -> (1,20) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -6894,26 +6672,30 @@ Score (1,1) -> (1,20) { } `; -exports[`AlphaTexParserTest valid-sync-points basic simple pitched: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points basic simple pitched: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points empty 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > empty 1`] = ` Score (1,1) -> (1,5) { - bars: Array [ + bars: [ Bar (1,2) -> (1,5), ], } `; -exports[`AlphaTexParserTest valid-sync-points empty with bars 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > empty with bars > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-sync-points > empty with bars > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-sync-points > empty with bars 1`] = ` Score (1,1) -> (1,13) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,6) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -6924,10 +6706,10 @@ Score (1,1) -> (1,13) { pipe: Pipe (1,7) -> (1,8), }, Bar (1,9) -> (1,13) { - beats: Array [ + beats: [ Beat (1,9) -> (1,13) { notes: NoteList (1,9) -> (1,11) { - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C5" (1,9) -> (1,11), }, @@ -6941,14 +6723,18 @@ Score (1,1) -> (1,13) { } `; -exports[`AlphaTexParserTest valid-sync-points empty with bars empty at end 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > empty with bars empty at end > lexer-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-sync-points > empty with bars empty at end > parser-diagnostics 1`] = `[]`; + +exports[`AlphaTexParserTest > valid-sync-points > empty with bars empty at end 1`] = ` Score (1,1) -> (1,15) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,6) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -6959,10 +6745,10 @@ Score (1,1) -> (1,15) { pipe: Pipe (1,7) -> (1,8), }, Bar (1,9) -> (1,13) { - beats: Array [ + beats: [ Beat (1,9) -> (1,11) { notes: NoteList (1,9) -> (1,11) { - notes: Array [ + notes: [ Note (1,9) -> (1,11) { noteValue: Ident "C5" (1,9) -> (1,11), }, @@ -6977,47 +6763,39 @@ Score (1,1) -> (1,15) { } `; -exports[`AlphaTexParserTest valid-sync-points empty with bars empty at end: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-sync-points empty with bars empty at end: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-sync-points empty with bars: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > full > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points empty with bars: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > full > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points empty: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-sync-points empty: parser-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-sync-points full 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > full 1`] = ` Score (1,1) -> (1,36) { - bars: Array [ + bars: [ Bar (1,2) -> (1,22) { - metaData: Array [ + metaData: [ Meta (1,2) -> (1,14) { tag: Tag "title" (1,2) -> (1,8) { prefix: Backslash (1,2) -> (1,3), tag: Ident "title" (1,3) -> (1,8), }, arguments: Arguments (1,9) -> (1,14) { - arguments: Array [ + arguments: [ String "Test" (1,9) -> (1,14) { parameterIndices: Map { 0 => 0, }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, }, }, ], - beats: Array [ + beats: [ Beat (1,18) -> (1,22) { notes: NoteList (1,18) -> (1,20) { - notes: Array [ + notes: [ Note (1,18) -> (1,20) { noteValue: Ident "C4" (1,18) -> (1,20), }, @@ -7029,14 +6807,14 @@ Score (1,1) -> (1,36) { ], }, Bar (1,23) -> (1,36) { - metaData: Array [ + metaData: [ Meta (1,25) -> (1,36) { tag: Tag "sync" (1,25) -> (1,30) { prefix: Backslash (1,25) -> (1,26), tag: Ident "sync" (1,26) -> (1,30), }, arguments: Arguments (1,31) -> (1,36) { - arguments: Array [ + arguments: [ Number "1" (1,31) -> (1,32) { parameterIndices: Map { 0 => 0, @@ -7053,7 +6831,7 @@ Score (1,1) -> (1,36) { }, }, ], - signatureCandidateIndices: Array [ + signatureCandidateIndices: [ 0, ], validated: true, @@ -7065,18 +6843,18 @@ Score (1,1) -> (1,36) { } `; -exports[`AlphaTexParserTest valid-sync-points full: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > properties empty > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points full: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > properties empty > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points properties empty 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > properties empty 1`] = ` Score (1,1) -> (1,25) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,8) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -7086,7 +6864,7 @@ Score (1,1) -> (1,25) { ], }, Bar (1,9) -> (1,25) { - metaData: Array [ + metaData: [ Meta (1,9) -> (1,25) { tag: Tag "sync" (1,9) -> (1,14) { prefix: Backslash (1,9) -> (1,10), @@ -7094,7 +6872,7 @@ Score (1,1) -> (1,25) { }, arguments: Arguments (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Number "1" (1,16) -> (1,17), Number "1" (1,18) -> (1,19), Number "1" (1,20) -> (1,21), @@ -7103,7 +6881,7 @@ Score (1,1) -> (1,25) { }, properties: Props (1,23) -> (1,25) { openBrace: LBrace (1,23) -> (1,24), - properties: Array [], + properties: [], closeBrace: RBrace (1,24) -> (1,25), }, }, @@ -7113,18 +6891,18 @@ Score (1,1) -> (1,25) { } `; -exports[`AlphaTexParserTest valid-sync-points properties empty: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > properties unknown > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points properties empty: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > properties unknown > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points properties unknown 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > properties unknown 1`] = ` Score (1,1) -> (1,42) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,8) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -7134,7 +6912,7 @@ Score (1,1) -> (1,42) { ], }, Bar (1,9) -> (1,42) { - metaData: Array [ + metaData: [ Meta (1,9) -> (1,42) { tag: Tag "sync" (1,9) -> (1,14) { prefix: Backslash (1,9) -> (1,10), @@ -7142,7 +6920,7 @@ Score (1,1) -> (1,42) { }, arguments: Arguments (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Number "1" (1,16) -> (1,17), Number "1" (1,18) -> (1,19), Number "1" (1,20) -> (1,21), @@ -7151,12 +6929,12 @@ Score (1,1) -> (1,42) { }, properties: Props (1,23) -> (1,42) { openBrace: LBrace (1,23) -> (1,24), - properties: Array [ + properties: [ Prop (1,25) -> (1,40) { property: Ident "unknown" (1,25) -> (1,32), properties: Arguments (1,33) -> (1,40) { openParenthesis: LParen (1,33) -> (1,34), - arguments: Array [ + arguments: [ Number "1" (1,34) -> (1,35), Number "2" (1,36) -> (1,37), Number "3" (1,38) -> (1,39), @@ -7174,18 +6952,18 @@ Score (1,1) -> (1,42) { } `; -exports[`AlphaTexParserTest valid-sync-points properties unknown: lexer-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > valuelist > lexer-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points properties unknown: parser-diagnostics 1`] = `Array []`; +exports[`AlphaTexParserTest > valid-sync-points > valuelist > parser-diagnostics 1`] = `[]`; -exports[`AlphaTexParserTest valid-sync-points valuelist 1`] = ` +exports[`AlphaTexParserTest > valid-sync-points > valuelist 1`] = ` Score (1,1) -> (1,22) { - bars: Array [ + bars: [ Bar (1,2) -> (1,8) { - beats: Array [ + beats: [ Beat (1,4) -> (1,8) { notes: NoteList (1,4) -> (1,6) { - notes: Array [ + notes: [ Note (1,4) -> (1,6) { noteValue: Ident "C4" (1,4) -> (1,6), }, @@ -7195,7 +6973,7 @@ Score (1,1) -> (1,22) { ], }, Bar (1,9) -> (1,22) { - metaData: Array [ + metaData: [ Meta (1,9) -> (1,22) { tag: Tag "sync" (1,9) -> (1,14) { prefix: Backslash (1,9) -> (1,10), @@ -7203,7 +6981,7 @@ Score (1,1) -> (1,22) { }, arguments: Arguments (1,15) -> (1,22) { openParenthesis: LParen (1,15) -> (1,16), - arguments: Array [ + arguments: [ Number "1" (1,16) -> (1,17), Number "1" (1,18) -> (1,19), Number "1" (1,20) -> (1,21), @@ -7216,7 +6994,3 @@ Score (1,1) -> (1,22) { ], } `; - -exports[`AlphaTexParserTest valid-sync-points valuelist: lexer-diagnostics 1`] = `Array []`; - -exports[`AlphaTexParserTest valid-sync-points valuelist: parser-diagnostics 1`] = `Array []`; diff --git a/packages/alphatab/test/importer/__snapshots__/Gp8Importer.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/Gp8Importer.test.ts.snap index 0fed28a05..d3b6b5de6 100644 --- a/packages/alphatab/test/importer/__snapshots__/Gp8Importer.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/Gp8Importer.test.ts.snap @@ -1,17 +1,17 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Gp8ImporterTest audio-track 1`] = ` +exports[`Gp8ImporterTest > audio-track 1`] = ` Map { "__kind" => "Score", "artist" => "JerryC", "music" => "JerryC", "title" => "Canon Rock", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 2, 2, 2, @@ -19,7 +19,7 @@ Map { ], }, }, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -29,7 +29,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -58,7 +58,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -124,7 +124,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -146,7 +146,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -156,7 +156,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -174,7 +174,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -328,7 +328,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -402,7 +402,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -707,13 +707,13 @@ Map { "timesignaturenumerator" => 6, "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 6, 6, ], }, }, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -723,7 +723,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -743,7 +743,7 @@ Map { "__kind" => "MasterBar", "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 2, 2, 2, @@ -751,7 +751,7 @@ Map { ], }, }, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -761,7 +761,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -783,7 +783,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -797,7 +797,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -811,7 +811,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -825,7 +825,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -839,7 +839,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -853,7 +853,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -867,7 +867,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -877,7 +877,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -899,7 +899,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -917,7 +917,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -979,7 +979,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -1025,7 +1025,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -1074,7 +1074,7 @@ Map { "timesignaturenumerator" => 6, "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 6, 6, ], @@ -1086,7 +1086,7 @@ Map { "__kind" => "MasterBar", "beamingrules" => Map { "groups" => Map { - "8" => Array [ + "8" => [ 2, 2, 2, @@ -1218,7 +1218,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1228,7 +1228,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -1258,7 +1258,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1268,7 +1268,7 @@ Map { "isvisible" => true, }, ], - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, @@ -1298,7 +1298,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1320,7 +1320,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1334,7 +1334,7 @@ Map { }, Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1376,7 +1376,7 @@ Map { }, Map { "__kind" => "MasterBar", - "syncpoints" => Array [ + "syncpoints" => [ Map { "islinear" => false, "type" => 4, diff --git a/packages/alphatab/test/importer/__snapshots__/MusicXmlImporter.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/MusicXmlImporter.test.ts.snap index b55cd2ecf..11e20342c 100644 --- a/packages/alphatab/test/importer/__snapshots__/MusicXmlImporter.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/MusicXmlImporter.test.ts.snap @@ -1,12 +1,12 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`MusicXmlImporterTests 2102-corrupt-direction 1`] = ` +exports[`MusicXmlImporterTests > 2102-corrupt-direction 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -18,25 +18,25 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -44,7 +44,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -79,14 +79,14 @@ Map { } `; -exports[`MusicXmlImporterTests barlines 1`] = ` +exports[`MusicXmlImporterTests > barlines 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "timesignaturecommon" => true, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -166,26 +166,26 @@ Map { "start" => 65280, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -207,11 +207,11 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -228,11 +228,11 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -249,11 +249,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -270,11 +270,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -291,11 +291,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -311,11 +311,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -331,11 +331,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -351,11 +351,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -371,11 +371,11 @@ Map { Map { "__kind" => "Bar", "id" => 9, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 9, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 9, @@ -391,11 +391,11 @@ Map { Map { "__kind" => "Bar", "id" => 10, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 10, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 10, @@ -411,11 +411,11 @@ Map { Map { "__kind" => "Bar", "id" => 11, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 11, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 11, @@ -431,11 +431,11 @@ Map { Map { "__kind" => "Bar", "id" => 12, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 12, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 12, @@ -451,11 +451,11 @@ Map { Map { "__kind" => "Bar", "id" => 13, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 13, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 13, @@ -471,11 +471,11 @@ Map { Map { "__kind" => "Bar", "id" => 14, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 14, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 14, @@ -491,11 +491,11 @@ Map { Map { "__kind" => "Bar", "id" => 15, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 15, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 15, @@ -511,11 +511,11 @@ Map { Map { "__kind" => "Bar", "id" => 16, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 16, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 16, @@ -531,11 +531,11 @@ Map { Map { "__kind" => "Bar", "id" => 17, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 17, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 17, @@ -562,7 +562,7 @@ Map { } `; -exports[`MusicXmlImporterTests buzzroll 1`] = ` +exports[`MusicXmlImporterTests > buzzroll 1`] = ` Map { "__kind" => "Score", "artist" => "Artist", @@ -572,10 +572,10 @@ Map { "title" => "Title", "words" => "Words", "tab" => "Tab", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -587,25 +587,25 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -613,7 +613,7 @@ Map { "tone" => 0, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -634,7 +634,7 @@ Map { Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -655,7 +655,7 @@ Map { Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -676,7 +676,7 @@ Map { Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -716,7 +716,7 @@ Map { } `; -exports[`MusicXmlImporterTests partwise-anacrusis 1`] = ` +exports[`MusicXmlImporterTests > partwise-anacrusis 1`] = ` Map { "__kind" => "Score", "artist" => "Artist", @@ -726,10 +726,10 @@ Map { "title" => "Title", "words" => "Words", "tab" => "Tab", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -758,26 +758,26 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -798,11 +798,11 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -817,11 +817,11 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -836,11 +836,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -856,11 +856,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -885,23 +885,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -922,11 +922,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -941,11 +941,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -960,11 +960,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -980,11 +980,11 @@ Map { Map { "__kind" => "Bar", "id" => 9, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 9, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 9, @@ -1016,7 +1016,7 @@ Map { } `; -exports[`MusicXmlImporterTests partwise-basic 1`] = ` +exports[`MusicXmlImporterTests > partwise-basic 1`] = ` Map { "__kind" => "Score", "artist" => "Artist", @@ -1026,10 +1026,10 @@ Map { "title" => "Title", "words" => "Words", "tab" => "Tab", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1049,26 +1049,26 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1088,11 +1088,11 @@ Map { Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -1107,11 +1107,11 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -1136,23 +1136,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1172,11 +1172,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -1191,11 +1191,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -1222,23 +1222,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1258,11 +1258,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -1277,11 +1277,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -1313,14 +1313,14 @@ Map { } `; -exports[`MusicXmlImporterTests partwise-complex-measures 1`] = ` +exports[`MusicXmlImporterTests > partwise-complex-measures 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "timesignaturecommon" => true, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1336,25 +1336,25 @@ Map { "start" => 3840, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -1368,7 +1368,7 @@ Map { "tone" => 2, }, ], - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1385,7 +1385,7 @@ Map { Map { "__kind" => "Beat", "id" => 1, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -1413,7 +1413,7 @@ Map { Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -1435,15 +1435,15 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 6, @@ -1464,7 +1464,7 @@ Map { Map { "__kind" => "Beat", "id" => 9, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 8, @@ -1492,7 +1492,7 @@ Map { Map { "__kind" => "Beat", "id" => 11, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 9, @@ -1516,15 +1516,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -1537,7 +1537,7 @@ Map { Map { "__kind" => "Beat", "id" => 5, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 4, @@ -1556,7 +1556,7 @@ Map { Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -1569,7 +1569,7 @@ Map { Map { "__kind" => "Beat", "id" => 7, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 5, @@ -1591,11 +1591,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 12, @@ -1608,7 +1608,7 @@ Map { Map { "__kind" => "Beat", "id" => 13, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 10, @@ -1627,7 +1627,7 @@ Map { Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 14, @@ -1640,7 +1640,7 @@ Map { Map { "__kind" => "Beat", "id" => 15, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 11, @@ -1671,24 +1671,24 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 16, "isempty" => true, "duration" => 256, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1705,7 +1705,7 @@ Map { Map { "__kind" => "Beat", "id" => 17, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 12, @@ -1725,7 +1725,7 @@ Map { Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 18, @@ -1738,7 +1738,7 @@ Map { Map { "__kind" => "Beat", "id" => 19, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 13, @@ -1757,7 +1757,7 @@ Map { Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 20, @@ -1770,7 +1770,7 @@ Map { Map { "__kind" => "Beat", "id" => 21, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 14, @@ -1793,11 +1793,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 9, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 22, @@ -1810,7 +1810,7 @@ Map { Map { "__kind" => "Beat", "id" => 23, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 15, @@ -1830,7 +1830,7 @@ Map { Map { "__kind" => "Voice", "id" => 10, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 24, @@ -1843,7 +1843,7 @@ Map { Map { "__kind" => "Beat", "id" => 25, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 16, @@ -1862,7 +1862,7 @@ Map { Map { "__kind" => "Voice", "id" => 11, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 26, @@ -1875,7 +1875,7 @@ Map { Map { "__kind" => "Beat", "id" => 27, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 17, @@ -1913,14 +1913,14 @@ Map { } `; -exports[`MusicXmlImporterTests partwise-staff-change 1`] = ` +exports[`MusicXmlImporterTests > partwise-staff-change 1`] = ` Map { "__kind" => "Score", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", "timesignaturecommon" => true, - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -1932,26 +1932,26 @@ Map { ], }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "duration" => 16, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -1979,7 +1979,7 @@ Map { Map { "__kind" => "Beat", "id" => 14, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 11, @@ -1999,7 +1999,7 @@ Map { Map { "__kind" => "Beat", "id" => 15, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 12, @@ -2019,7 +2019,7 @@ Map { Map { "__kind" => "Beat", "id" => 16, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 13, @@ -2040,7 +2040,7 @@ Map { Map { "__kind" => "Beat", "id" => 17, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 14, @@ -2067,16 +2067,16 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, "clef" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -2089,7 +2089,7 @@ Map { Map { "__kind" => "Beat", "id" => 2, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 0, @@ -2109,7 +2109,7 @@ Map { Map { "__kind" => "Beat", "id" => 3, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 1, @@ -2130,7 +2130,7 @@ Map { Map { "__kind" => "Beat", "id" => 4, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 2, @@ -2150,7 +2150,7 @@ Map { Map { "__kind" => "Beat", "id" => 5, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 3, @@ -2170,7 +2170,7 @@ Map { Map { "__kind" => "Beat", "id" => 6, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 4, @@ -2190,7 +2190,7 @@ Map { Map { "__kind" => "Beat", "id" => 7, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 5, @@ -2210,7 +2210,7 @@ Map { Map { "__kind" => "Beat", "id" => 8, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 6, @@ -2230,7 +2230,7 @@ Map { Map { "__kind" => "Beat", "id" => 9, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 7, @@ -2251,7 +2251,7 @@ Map { Map { "__kind" => "Beat", "id" => 10, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 8, @@ -2271,7 +2271,7 @@ Map { Map { "__kind" => "Beat", "id" => 11, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 9, @@ -2291,7 +2291,7 @@ Map { Map { "__kind" => "Beat", "id" => 12, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 10, @@ -2313,7 +2313,7 @@ Map { Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 47, @@ -2326,7 +2326,7 @@ Map { Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 48, @@ -2339,7 +2339,7 @@ Map { Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 49, @@ -2352,11 +2352,11 @@ Map { Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 18, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 15, @@ -2376,7 +2376,7 @@ Map { Map { "__kind" => "Beat", "id" => 19, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 16, @@ -2397,7 +2397,7 @@ Map { Map { "__kind" => "Beat", "id" => 20, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 17, @@ -2418,7 +2418,7 @@ Map { Map { "__kind" => "Beat", "id" => 21, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 18, @@ -2439,7 +2439,7 @@ Map { Map { "__kind" => "Beat", "id" => 22, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 19, @@ -2460,7 +2460,7 @@ Map { Map { "__kind" => "Beat", "id" => 23, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 20, @@ -2481,7 +2481,7 @@ Map { Map { "__kind" => "Beat", "id" => 24, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 21, @@ -2502,7 +2502,7 @@ Map { Map { "__kind" => "Beat", "id" => 25, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 22, @@ -2537,23 +2537,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 26, "duration" => 16, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -2581,7 +2581,7 @@ Map { Map { "__kind" => "Beat", "id" => 36, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 30, @@ -2621,7 +2621,7 @@ Map { Map { "__kind" => "Beat", "id" => 42, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 33, @@ -2641,7 +2641,7 @@ Map { Map { "__kind" => "Beat", "id" => 43, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 34, @@ -2661,7 +2661,7 @@ Map { Map { "__kind" => "Beat", "id" => 44, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 35, @@ -2681,7 +2681,7 @@ Map { Map { "__kind" => "Beat", "id" => 45, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 36, @@ -2701,7 +2701,7 @@ Map { Map { "__kind" => "Beat", "id" => 46, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 37, @@ -2727,15 +2727,15 @@ Map { }, Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 27, @@ -2748,7 +2748,7 @@ Map { Map { "__kind" => "Beat", "id" => 28, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 23, @@ -2768,7 +2768,7 @@ Map { Map { "__kind" => "Beat", "id" => 29, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 24, @@ -2788,7 +2788,7 @@ Map { Map { "__kind" => "Beat", "id" => 30, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 25, @@ -2808,7 +2808,7 @@ Map { Map { "__kind" => "Beat", "id" => 31, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 26, @@ -2828,7 +2828,7 @@ Map { Map { "__kind" => "Beat", "id" => 32, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 27, @@ -2848,7 +2848,7 @@ Map { Map { "__kind" => "Beat", "id" => 33, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 28, @@ -2868,7 +2868,7 @@ Map { Map { "__kind" => "Beat", "id" => 34, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 29, @@ -2899,7 +2899,7 @@ Map { Map { "__kind" => "Beat", "id" => 38, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 31, @@ -2919,7 +2919,7 @@ Map { Map { "__kind" => "Beat", "id" => 39, - "notes" => Array [ + "notes" => [ Map { "__kind" => "Note", "id" => 32, @@ -2958,7 +2958,7 @@ Map { } `; -exports[`MusicXmlImporterTests timewise-anacrusis 1`] = ` +exports[`MusicXmlImporterTests > timewise-anacrusis 1`] = ` Map { "__kind" => "Score", "artist" => "Artist", @@ -2968,10 +2968,10 @@ Map { "title" => "Title", "words" => "Words", "tab" => "Tab", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3000,26 +3000,26 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3040,11 +3040,11 @@ Map { Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -3059,11 +3059,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -3078,11 +3078,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, @@ -3098,11 +3098,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -3127,23 +3127,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3164,11 +3164,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, @@ -3183,11 +3183,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -3202,11 +3202,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, @@ -3222,11 +3222,11 @@ Map { Map { "__kind" => "Bar", "id" => 9, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 9, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 9, @@ -3258,7 +3258,7 @@ Map { } `; -exports[`MusicXmlImporterTests timewise-basic 1`] = ` +exports[`MusicXmlImporterTests > timewise-basic 1`] = ` Map { "__kind" => "Score", "artist" => "Artist", @@ -3268,10 +3268,10 @@ Map { "title" => "Title", "words" => "Words", "tab" => "Tab", - "masterbars" => Array [ + "masterbars" => [ Map { "__kind" => "MasterBar", - "tempoautomations" => Array [ + "tempoautomations" => [ Map { "islinear" => false, "type" => 0, @@ -3291,26 +3291,26 @@ Map { "start" => 7680, }, ], - "tracks" => Array [ + "tracks" => [ Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 0, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 0, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 0, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3330,11 +3330,11 @@ Map { Map { "__kind" => "Bar", "id" => 3, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 3, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 1, @@ -3349,11 +3349,11 @@ Map { Map { "__kind" => "Bar", "id" => 6, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 6, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 2, @@ -3378,23 +3378,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 1, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 1, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 3, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3414,11 +3414,11 @@ Map { Map { "__kind" => "Bar", "id" => 4, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 4, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 4, @@ -3433,11 +3433,11 @@ Map { Map { "__kind" => "Bar", "id" => 7, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 7, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 5, @@ -3464,23 +3464,23 @@ Map { }, Map { "__kind" => "Track", - "staves" => Array [ + "staves" => [ Map { "__kind" => "Staff", - "bars" => Array [ + "bars" => [ Map { "__kind" => "Bar", "id" => 2, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 2, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 6, "isempty" => true, - "automations" => Array [ + "automations" => [ Map { "islinear" => false, "type" => 2, @@ -3500,11 +3500,11 @@ Map { Map { "__kind" => "Bar", "id" => 5, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 5, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 7, @@ -3519,11 +3519,11 @@ Map { Map { "__kind" => "Bar", "id" => 8, - "voices" => Array [ + "voices" => [ Map { "__kind" => "Voice", "id" => 8, - "beats" => Array [ + "beats" => [ Map { "__kind" => "Beat", "id" => 8, diff --git a/packages/alphatab/test/mocha.jest-snapshot.ts b/packages/alphatab/test/mocha.jest-snapshot.ts deleted file mode 100644 index 7248c866e..000000000 --- a/packages/alphatab/test/mocha.jest-snapshot.ts +++ /dev/null @@ -1,513 +0,0 @@ -/** @target web */ -import chalk from 'chalk'; -import * as chai from 'chai'; -import url from 'node:url'; -import path from 'node:path'; -import { - addSerializer, - buildSnapshotResolver, - type SnapshotResolver, - SnapshotState, - toMatchSnapshot -} from 'jest-snapshot'; -import type { Config } from '@jest/types'; -import slash from 'slash'; -import type { SyncExpectationResult } from 'expect'; -import { equals, iterableEquality, subsetEquality } from '@jest/expect-utils'; -import * as matcherUtils from 'jest-matcher-utils'; -import { - AlphaTexAstNodePlugin, - AlphaTexDiagnosticPlugin, - MidiEventSerializerPlugin, - type PrettyFormatConfig, - type PrettyFormatPrinter, - ScoreSerializerPlugin -} from './PrettyFormat'; - -// Mocha and Chai integration (called from global-hooks.ts) -declare global { - namespace Chai { - interface Assertion { - toMatchSnapshot(message?: string): Assertion; - } - } -} - -export async function initializeJestSnapshot() { - // Setup jest-snapshot - - addSerializer({ - test(val) { - return ScoreSerializerPlugin.instance.test(val); - }, - serialize(val, config, indentation, depth, refs, printer) { - // - return ScoreSerializerPlugin.instance.serialize( - val, - config as PrettyFormatConfig, - indentation, - depth, - refs, - printer as PrettyFormatPrinter - ); - } - }); - addSerializer({ - test(val) { - return MidiEventSerializerPlugin.instance.test(val); - }, - serialize(val, config, indentation, depth, refs, printer) { - // - return MidiEventSerializerPlugin.instance.serialize( - val, - config as PrettyFormatConfig, - indentation, - depth, - refs, - printer as PrettyFormatPrinter - ); - } - }); - addSerializer({ - test(val) { - return AlphaTexDiagnosticPlugin.instance.test(val); - }, - serialize(val, config, indentation, depth, refs, printer) { - // - return AlphaTexDiagnosticPlugin.instance.serialize( - val, - config as PrettyFormatConfig, - indentation, - depth, - refs, - printer as PrettyFormatPrinter - ); - } - }); - addSerializer({ - test(val) { - return AlphaTexAstNodePlugin.instance.test(val); - }, - serialize(val, config, indentation, depth, refs, printer) { - // - return AlphaTexAstNodePlugin.instance.serialize( - val, - config as PrettyFormatConfig, - indentation, - depth, - refs, - printer as PrettyFormatPrinter - ); - } - }); - - currentResolver = await buildSnapshotResolver(globalConfig); - - chai.use((chai, utils) => { - utils.addMethod( - chai.Assertion.prototype, - 'toMatchSnapshot', - function (this: Record, message?: string) { - const received = utils.flag(this, 'object'); - const isNot = utils.flag(this, 'negate') as boolean; - - const args = [received]; - if (message !== undefined) { - args.push(message); - } - - const matchResult = toMatchSnapshot.apply( - { - // Context - snapshotState: snapshotState!, - - // MatcherContext -> MatcherState - assertionCalls, - currentConcurrentTestName: undefined, - currentTestName: currentTest!.fullTitle(), - error: undefined, - expand: snapshotOptions.expand, - expectedAssertionsNumber: null, - expectedAssertionsNumberError: undefined, - isExpectingAssertions: false, - isExpectingAssertionsError: undefined, - isNot, - numPassingAsserts: 0, - promise: undefined, - suppressedErrors: [], - testPath: currentTest!.file, - - // MatcherContext -> MatcherUtils - customTesters: [], - dontThrow() {}, - equals, - utils: { - ...matcherUtils, - iterableEquality, - subsetEquality - } - }, - args as any - ) as SyncExpectationResult; - - assertionCalls++; - - if (!matchResult.pass) { - chai.assert.fail(matchResult.message()); - } - } - ); - }); -} - -export function beforeEachTest(newTest: Mocha.Test) { - if (currentTest === undefined || currentTest?.file !== newTest.file) { - if (snapshotState) { - storeSnapshotState(snapshotState); - } - - snapshotState = new SnapshotState(currentResolver!.resolveSnapshotPath(newTest.file!), snapshotOptions); - } - assertionCalls = 0; - currentTest = newTest; - executedTestNames.add(newTest.fullTitle()); -} - -export function afterAll() { - if (snapshotState) { - storeSnapshotState(snapshotState); - } - - writeSummaryReport(); -} - -// -// Adapted code from Jest for the use in Mocha -// MIT License -// Copyright (c) Meta Platforms, Inc. and affiliates. -// Copyright Contributors to the Jest project. -// https://github.com/jestjs/jest/blob/main/LICENSE - -// General Snapshot matching -const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); - -type SnapshotStateOptions = ConstructorParameters[1]; -type SnapshotSummary = { - added: number; - didUpdate: boolean; - failure: boolean; - filesAdded: number; - filesRemoved: number; - filesRemovedList: Array; - filesUnmatched: number; - filesUpdated: number; - matched: number; - total: number; - unchecked: number; - uncheckedKeysByFile: Array; - unmatched: number; - updated: number; -}; -type UncheckedSnapshot = { - filePath: string; - keys: Array; -}; - -const globalConfig: Config.ProjectConfig = { - rootDir: path.resolve(__dirname, '..'), - snapshotFormat: {}, - // defaults - automock: false, - cache: false, - cacheDirectory: '', - clearMocks: false, - collectCoverageFrom: [], - coverageDirectory: '', - coveragePathIgnorePatterns: [], - cwd: '', - dependencyExtractor: undefined, - detectLeaks: false, - detectOpenHandles: false, - displayName: undefined, - errorOnDeprecated: false, - extensionsToTreatAsEsm: [], - fakeTimers: {}, - filter: undefined, - forceCoverageMatch: [], - globalSetup: undefined, - globalTeardown: undefined, - globals: {}, - haste: {}, - id: '', - injectGlobals: false, - moduleDirectories: [], - moduleFileExtensions: [], - moduleNameMapper: [], - modulePathIgnorePatterns: [], - modulePaths: undefined, - openHandlesTimeout: 0, - preset: undefined, - prettierPath: '', - resetMocks: false, - resetModules: false, - resolver: undefined, - restoreMocks: false, - roots: [], - runner: '', - runtime: undefined, - sandboxInjectedGlobals: [], - setupFiles: [], - setupFilesAfterEnv: [], - skipFilter: false, - skipNodeResolution: undefined, - slowTestThreshold: 0, - snapshotResolver: undefined, - snapshotSerializers: [], - testEnvironment: '', - testEnvironmentOptions: {}, - testMatch: [], - testLocationInResults: false, - testPathIgnorePatterns: [], - testRegex: [], - testRunner: '', - transform: [], - transformIgnorePatterns: [], - watchPathIgnorePatterns: [], - unmockedModulePathPatterns: undefined, - workerIdleMemoryLimit: undefined, - coverageReporters: [], - reporters: [], - testTimeout: 5000, - waitForUnhandledRejections: false -}; - -// https://github.com/jestjs/jest/blob/4e7d916ec6a16de5548273c17b5d2c5761b0aebb/packages/jest-config/src/normalize.ts#L1079-L1088 -const argvCi = !!process.env.CI; -const argvUpdateSnapshot = process.argv.includes('--updateSnapshot') || process.env.UPDATE_SNAPSHOT === 'true'; -const snapshotOptions: SnapshotStateOptions = { - updateSnapshot: argvCi ? 'none' : argvUpdateSnapshot ? 'all' : 'new', - rootDir: globalConfig.rootDir, - snapshotFormat: globalConfig.snapshotFormat, - expand: undefined, - prettierPath: undefined -}; - -let currentResolver: SnapshotResolver | undefined; -let currentTest: Mocha.Test | undefined = undefined; -let snapshotState: SnapshotState | undefined = undefined; -let assertionCalls = 0; - -// Snapshot results -const executedTestNames = new Set(); -const aggregatedResults: SnapshotSummary = { - // https://github.com/jestjs/jest/blob/4e7d916ec6a16de5548273c17b5d2c5761b0aebb/packages/jest-test-result/src/helpers.ts#L22 - added: 0, - didUpdate: false, // is set only after the full run - failure: false, - filesAdded: 0, - // combines individual test results + removed files after the full run - filesRemoved: 0, - filesRemovedList: [], - filesUnmatched: 0, - filesUpdated: 0, - matched: 0, - total: 0, - unchecked: 0, - uncheckedKeysByFile: [], - unmatched: 0, - updated: 0 -}; - -function storeSnapshotState(snapshotState: SnapshotState) { - // https://github.com/jestjs/jest/blob/4e7d916ec6a16de5548273c17b5d2c5761b0aebb/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts#L137 - - const uncheckedCount = snapshotState.getUncheckedCount(); - const uncheckedKeys = snapshotState.getUncheckedKeys(); - // if (uncheckedCount) { - // snapshotState.removeUncheckedKeys(); - // } - const status = snapshotState.save(); - - // https://github.com/jestjs/jest/blob/4e7d916ec6a16de5548273c17b5d2c5761b0aebb/packages/jest-test-result/src/helpers.ts#L119C1-L120C1 - // Snapshot data - - if (snapshotState.added) { - aggregatedResults.filesAdded++; - } - if (status.deleted) { - aggregatedResults.filesRemoved++; - } - if (snapshotState.unmatched) { - aggregatedResults.filesUnmatched++; - } - if (snapshotState.updated) { - aggregatedResults.filesUpdated++; - } - - aggregatedResults.unmatched += snapshotState.unmatched; - aggregatedResults.updated += snapshotState.updated; - aggregatedResults.total += - snapshotState.added + snapshotState.matched + snapshotState.unmatched + snapshotState.updated; - - aggregatedResults.added += snapshotState.added; - aggregatedResults.matched += snapshotState.matched; - aggregatedResults.unchecked += status.deleted ? 0 : uncheckedCount; - if (uncheckedKeys.length > 0) { - aggregatedResults.uncheckedKeysByFile.push({ - filePath: currentTest!.file!, - keys: uncheckedKeys - }); - } - - aggregatedResults.filesAdded += snapshotState.added; - aggregatedResults.filesRemoved += uncheckedCount; - aggregatedResults.filesUnmatched += snapshotState.unmatched; -} - -// Result Summary Printing -// https://github.com/jestjs/jest/blob/4e7d916ec6a16de5548273c17b5d2c5761b0aebb/packages/jest-reporters/src/getSnapshotSummary.ts#L25 -const ARROW = ' \u203A '; -const DOWN_ARROW = ' \u21B3 '; -const DOT = ' \u2022 '; -const FAIL_COLOR = chalk.bold.red; -const OBSOLETE_COLOR = chalk.bold.yellow; -const SNAPSHOT_ADDED = chalk.bold.green; -const SNAPSHOT_NOTE = chalk.dim; -const SNAPSHOT_REMOVED = chalk.bold.green; -const SNAPSHOT_SUMMARY = chalk.bold; -const SNAPSHOT_UPDATED = chalk.bold.green; - -function pluralize(word: string, count: number, ending = 's'): string { - return `${count} ${word}${count === 1 ? '' : ending}`; -} - -function relativePath( - config: Config.GlobalConfig | Config.ProjectConfig, - testPath: string -): { basename: string; dirname: string } { - // this function can be called with ProjectConfigs or GlobalConfigs. GlobalConfigs - // do not have config.cwd, only config.rootDir. Try using config.cwd, fallback - // to config.rootDir. (Also, some unit just use config.rootDir, which is ok) - testPath = path.relative((config as Config.ProjectConfig).cwd || config.rootDir, testPath); - const dirname = path.dirname(testPath); - const basename = path.basename(testPath); - return { basename, dirname }; -} - -function formatTestPath(config: Config.GlobalConfig | Config.ProjectConfig, testPath: string): string { - const { dirname, basename } = relativePath(config, testPath); - return slash(chalk.dim(dirname + path.sep) + chalk.bold(basename)); -} - -function writeSummaryReport() { - const snapshots = aggregatedResults; - const updateCommand = '--updateSnapshots'; - - // filter out obsolete keys if we did not execute the related test - for (const uncheckedFile of snapshots.uncheckedKeysByFile) { - const keysToRemove = new Set(); - for (const key of uncheckedFile.keys) { - let removeKey = true; - for (const executed of executedTestNames) { - if (key.startsWith(executed)) { - removeKey = false; - break; - } - } - - if (removeKey) { - keysToRemove.add(key); - snapshots.unchecked--; - snapshots.filesRemoved--; - } - } - - uncheckedFile.keys = uncheckedFile.keys.filter(k => !keysToRemove.has(k)); - } - - snapshots.uncheckedKeysByFile = snapshots.uncheckedKeysByFile.filter(f => f.keys.length > 0); - - const summary: string[] = []; - summary.push(SNAPSHOT_SUMMARY('Snapshot Summary')); - if (snapshots.added) { - summary.push( - `${SNAPSHOT_ADDED( - `${ARROW + pluralize('snapshot', snapshots.added)} written ` - )}from ${pluralize('test suite', snapshots.filesAdded)}.` - ); - } - - if (snapshots.unmatched) { - summary.push( - `${FAIL_COLOR(`${ARROW}${pluralize('snapshot', snapshots.unmatched)} failed`)} from ${pluralize( - 'test suite', - snapshots.filesUnmatched - )}. ${SNAPSHOT_NOTE(`Inspect your code changes or ${updateCommand} to update them.`)}` - ); - } - - if (snapshots.updated) { - summary.push( - `${SNAPSHOT_UPDATED( - `${ARROW + pluralize('snapshot', snapshots.updated)} updated ` - )}from ${pluralize('test suite', snapshots.filesUpdated)}.` - ); - } - - if (snapshots.filesRemoved) { - if (snapshots.didUpdate) { - summary.push( - `${SNAPSHOT_REMOVED( - `${ARROW}${pluralize('snapshot file', snapshots.filesRemoved)} removed ` - )}from ${pluralize('test suite', snapshots.filesRemoved)}.` - ); - } else { - summary.push( - `${OBSOLETE_COLOR( - `${ARROW}${pluralize('snapshot file', snapshots.filesRemoved)} obsolete ` - )}from ${pluralize('test suite', snapshots.filesRemoved)}. ${SNAPSHOT_NOTE( - `To remove ${snapshots.filesRemoved === 1 ? 'it' : 'them all'}, ${updateCommand}.` - )}` - ); - } - } - if (snapshots.filesRemovedList && snapshots.filesRemovedList.length > 0) { - const [head, ...tail] = snapshots.filesRemovedList; - summary.push(` ${DOWN_ARROW} ${DOT}${formatTestPath(globalConfig, head)}`); - - for (const key of tail) { - summary.push(` ${DOT}${formatTestPath(globalConfig, key)}`); - } - } - - if (snapshots.unchecked) { - if (snapshots.didUpdate) { - summary.push( - `${SNAPSHOT_REMOVED(`${ARROW}${pluralize('snapshot', snapshots.unchecked)} removed `)}from ${pluralize( - 'test suite', - snapshots.uncheckedKeysByFile.length - )}.` - ); - } else { - summary.push( - `${OBSOLETE_COLOR(`${ARROW}${pluralize('snapshot', snapshots.unchecked)} obsolete `)}from ${pluralize( - 'test suite', - snapshots.uncheckedKeysByFile.length - )}. ${SNAPSHOT_NOTE(`To remove ${snapshots.unchecked === 1 ? 'it' : 'them all'}, ${updateCommand}.`)}` - ); - } - - for (const uncheckedFile of snapshots.uncheckedKeysByFile) { - summary.push(` ${DOWN_ARROW}${formatTestPath(globalConfig, uncheckedFile.filePath)}`); - - for (const key of uncheckedFile.keys) { - summary.push(` ${DOT}${key}`); - } - } - } - - console.log(); - for (const line of summary) { - console.log(line); - } -} diff --git a/packages/alphatab/test/model/AccidentalResolutionEdge.test.ts b/packages/alphatab/test/model/AccidentalResolutionEdge.test.ts index 6ee0a080d..03d7025cd 100644 --- a/packages/alphatab/test/model/AccidentalResolutionEdge.test.ts +++ b/packages/alphatab/test/model/AccidentalResolutionEdge.test.ts @@ -1,36 +1,35 @@ +import { describe, expect, it } from 'vitest'; import { AccidentalType } from '@coderline/alphatab/model/AccidentalType'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; -import { expect } from 'chai'; - describe('AccidentalResolutionEdgeTests', () => { it('spells B# in C# major for pitch C natural', () => { const ks = KeySignature.CSharp; const noteValue = 60; // C4 const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); - expect(spelling.degree).to.equal(6); // B - expect(spelling.accidentalOffset).to.equal(1); // B# + expect(spelling.degree).toBe(6); // B + expect(spelling.accidentalOffset).toBe(1); // B# const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, false, null); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('spells Fb in Cb major for pitch E natural', () => { const ks = KeySignature.Cb; const noteValue = 64; // E4 const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); - expect(spelling.degree).to.equal(3); // F - expect(spelling.accidentalOffset).to.equal(-1); // Fb + expect(spelling.degree).toBe(3); // F + expect(spelling.accidentalOffset).toBe(-1); // Fb const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, false, null); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('forces double sharp spelling when requested', () => { const ks = KeySignature.C; const noteValue = 62; // D const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceDoubleSharp); - expect(spelling.degree).to.equal(0); // C - expect(spelling.accidentalOffset).to.equal(2); // C## + expect(spelling.degree).toBe(0); // C + expect(spelling.accidentalOffset).toBe(2); // C## const accidental = ModelUtils.computeAccidentalForSpelling( ks, NoteAccidentalMode.ForceDoubleSharp, @@ -38,15 +37,15 @@ describe('AccidentalResolutionEdgeTests', () => { false, null ); - expect(accidental).to.equal(AccidentalType.DoubleSharp); + expect(accidental).toBe(AccidentalType.DoubleSharp); }); it('forces double flat spelling when requested', () => { const ks = KeySignature.C; const noteValue = 62; // D const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceDoubleFlat); - expect(spelling.degree).to.equal(2); // E - expect(spelling.accidentalOffset).to.equal(-2); // Ebb + expect(spelling.degree).toBe(2); // E + expect(spelling.accidentalOffset).toBe(-2); // Ebb const accidental = ModelUtils.computeAccidentalForSpelling( ks, NoteAccidentalMode.ForceDoubleFlat, @@ -54,6 +53,6 @@ describe('AccidentalResolutionEdgeTests', () => { false, null ); - expect(accidental).to.equal(AccidentalType.DoubleFlat); + expect(accidental).toBe(AccidentalType.DoubleFlat); }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/AccidentalResolutionTests.test.ts b/packages/alphatab/test/model/AccidentalResolutionTests.test.ts index a498eb6c8..c2d0dcfb8 100644 --- a/packages/alphatab/test/model/AccidentalResolutionTests.test.ts +++ b/packages/alphatab/test/model/AccidentalResolutionTests.test.ts @@ -1,9 +1,8 @@ +import { describe, expect, it } from 'vitest'; import { AccidentalType } from '@coderline/alphatab/model/AccidentalType'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; -import { expect } from 'chai'; - describe('AccidentalResolutionTests', () => { const degreeSemitones = [0, 2, 4, 5, 7, 9, 11]; @@ -36,8 +35,8 @@ describe('AccidentalResolutionTests', () => { for (let degree = 0; degree < 7; degree++) { const noteValue = noteValueForDegree(ks, degree, 4); const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); - expect(spelling.degree, `ks=${ks} degree=${degree}`).to.equal(degree); - expect(spelling.accidentalOffset, `ks=${ks} degree=${degree}`).to.equal( + expect(spelling.degree, `ks=${ks} degree=${degree}`).toBe(degree); + expect(spelling.accidentalOffset, `ks=${ks} degree=${degree}`).toBe( ModelUtils.getKeySignatureAccidentalOffset(ks, degree) ); @@ -48,7 +47,7 @@ describe('AccidentalResolutionTests', () => { false, null ); - expect(accidental, `ks=${ks} degree=${degree}`).to.equal(AccidentalType.None); + expect(accidental, `ks=${ks} degree=${degree}`).toBe(AccidentalType.None); } } }); @@ -57,50 +56,50 @@ describe('AccidentalResolutionTests', () => { const ks = KeySignature.FSharp; const noteValue = 65; // F natural const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); - expect(spelling.degree).to.equal(2); // E - expect(spelling.accidentalOffset).to.equal(1); // E# + expect(spelling.degree).toBe(2); // E + expect(spelling.accidentalOffset).toBe(1); // E# const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, false, null); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('spells Cb in Cb major for pitch B natural', () => { const ks = KeySignature.Cb; const noteValue = 59; // B natural const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); - expect(spelling.degree).to.equal(0); // C - expect(spelling.accidentalOffset).to.equal(-1); // Cb + expect(spelling.degree).toBe(0); // C + expect(spelling.accidentalOffset).toBe(-1); // Cb const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, false, null); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('forces flat spelling preference when requested', () => { const ks = KeySignature.C; const noteValue = 61; // C# / Db const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceFlat); - expect(spelling.degree).to.equal(1); // D - expect(spelling.accidentalOffset).to.equal(-1); // Db + expect(spelling.degree).toBe(1); // D + expect(spelling.accidentalOffset).toBe(-1); // Db const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceFlat, spelling, false, null); - expect(accidental).to.equal(AccidentalType.Flat); + expect(accidental).toBe(AccidentalType.Flat); }); it('forces sharp spelling preference when requested', () => { const ks = KeySignature.C; const noteValue = 61; // C# / Db const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceSharp); - expect(spelling.degree).to.equal(0); // C - expect(spelling.accidentalOffset).to.equal(1); // C# + expect(spelling.degree).toBe(0); // C + expect(spelling.accidentalOffset).toBe(1); // C# const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceSharp, spelling, false, null); - expect(accidental).to.equal(AccidentalType.Sharp); + expect(accidental).toBe(AccidentalType.Sharp); }); it('force natural displays a natural accidental when key signature would otherwise apply one', () => { const ks = KeySignature.D; // F#, C# const noteValue = 65; // F natural const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceNatural); - expect(spelling.degree).to.equal(3); // F - expect(spelling.accidentalOffset).to.equal(0); // natural + expect(spelling.degree).toBe(3); // F + expect(spelling.accidentalOffset).toBe(0); // natural const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceNatural, spelling, false, null); - expect(accidental).to.equal(AccidentalType.Natural); + expect(accidental).toBe(AccidentalType.Natural); }); it('force none suppresses accidentals regardless of spelling', () => { @@ -108,7 +107,7 @@ describe('AccidentalResolutionTests', () => { const noteValue = 61; // C# const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceNone); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceNone, spelling, false, null); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('no accidental when current accidental already matches', () => { @@ -116,7 +115,7 @@ describe('AccidentalResolutionTests', () => { const noteValue = 61; // C# const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, false, 1); - expect(accidental).to.equal(AccidentalType.None); + expect(accidental).toBe(AccidentalType.None); }); it('quarter tone accidentals are chosen when quarter bend is true', () => { @@ -124,6 +123,6 @@ describe('AccidentalResolutionTests', () => { const noteValue = 61; // C# -> requires sharp const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, true, null); - expect(accidental).to.equal(AccidentalType.SharpQuarterNoteUp); + expect(accidental).toBe(AccidentalType.SharpQuarterNoteUp); }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/Beat.test.ts b/packages/alphatab/test/model/Beat.test.ts index 27cc0d294..fb34910b2 100644 --- a/packages/alphatab/test/model/Beat.test.ts +++ b/packages/alphatab/test/model/Beat.test.ts @@ -1,16 +1,15 @@ +import { describe, expect, it } from 'vitest'; import { Beat } from '@coderline/alphatab/model/Beat'; import { Note } from '@coderline/alphatab/model/Note'; -import { expect } from 'chai'; - describe('BeatTests', () => { it('add-stringed-note', () => { const beat = new Beat(); const note = new Note(); note.string = 2; beat.addNote(note); - expect(beat.notes.length).to.be.equal(1); - expect(beat.hasNoteOnString(2)).to.be.equal(true); - expect(beat.getNoteOnString(2)).to.be.equal(note); + expect(beat.notes.length).toBe(1); + expect(beat.hasNoteOnString(2)).toBe(true); + expect(beat.getNoteOnString(2)).toBe(note); }); it('remove-stringed-note', () => { @@ -19,8 +18,8 @@ describe('BeatTests', () => { note.string = 1; beat.addNote(note); beat.removeNote(note); - expect(beat.notes.length).to.be.equal(0); - expect(beat.hasNoteOnString(2)).to.be.equal(false); - expect(beat.getNoteOnString(2)).to.be.equal(null); + expect(beat.notes.length).toBe(0); + expect(beat.hasNoteOnString(2)).toBe(false); + expect(beat.getNoteOnString(2)).toBe(null); }); }); diff --git a/packages/alphatab/test/model/Color.test.ts b/packages/alphatab/test/model/Color.test.ts index 80c56c0bd..33240a0d5 100644 --- a/packages/alphatab/test/model/Color.test.ts +++ b/packages/alphatab/test/model/Color.test.ts @@ -1,46 +1,45 @@ +import { describe, expect, it } from 'vitest'; import { Color } from '@coderline/alphatab/model/Color'; -import { expect } from 'chai'; - describe('ColorTests', () => { it('fromJson-number', () => { const color = Color.fromJson(-635386); - expect(color!.rgba).to.equal('#F64E06'); + expect(color!.rgba).toBe('#F64E06'); }); it('fromJson-#RGB', () => { const color = Color.fromJson('#0f0'); - expect(color!.rgba).to.equal('#00FF00'); + expect(color!.rgba).toBe('#00FF00'); }); it('fromJson-#RGBA', () => { const color = Color.fromJson('#0f09'); - expect(color!.rgba).to.equal('rgba(0,255,0,0.6)'); + expect(color!.rgba).toBe('rgba(0,255,0,0.6)'); }); it('fromJson-#RRGGBB', () => { const color = Color.fromJson('#f64e06'); - expect(color!.rgba).to.equal('#F64E06'); + expect(color!.rgba).toBe('#F64E06'); }); it('fromJson-#RRGGBBAA', () => { const color = Color.fromJson('#f64e0600'); - expect(color!.rgba).to.equal('rgba(246,78,6,0)'); + expect(color!.rgba).toBe('rgba(246,78,6,0)'); }); it('fromJson-rgb', () => { const color = Color.fromJson('rgb(246,78,6)'); - expect(color!.rgba).to.equal('#F64E06'); + expect(color!.rgba).toBe('#F64E06'); }); it('fromJson-rgba', () => { const color = Color.fromJson('rgba(246,78,6,0)'); - expect(color!.rgba).to.equal('rgba(246,78,6,0)'); + expect(color!.rgba).toBe('rgba(246,78,6,0)'); }); }); diff --git a/packages/alphatab/test/model/ComparisonHelpers.ts b/packages/alphatab/test/model/ComparisonHelpers.ts index 8b022b5a7..0ca8b167a 100644 --- a/packages/alphatab/test/model/ComparisonHelpers.ts +++ b/packages/alphatab/test/model/ComparisonHelpers.ts @@ -2,8 +2,6 @@ import { JsonConverter } from '@coderline/alphatab/model/JsonConverter'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import type { Score } from '@coderline/alphatab/model/Score'; import { TestPlatform } from 'test/TestPlatform'; -import { assert } from 'chai'; - /** * @partial * @internal @@ -142,36 +140,36 @@ export class ComparisonHelpers { const actualType = typeof actual; if (actualType !== expectedType) { - assert.fail(`Type Mismatch on hierarchy: ${path}, actual<'${actualType}'> != expected<'${expectedType}'>`); + throw new Error(`Type Mismatch on hierarchy: ${path}, actual<'${actualType}'> != expected<'${expectedType}'>`); } switch (actualType) { case 'boolean': if ((actual as boolean) !== (expected as boolean)) { - assert.fail( + throw new Error( `Boolean mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>` ); } break; case 'number': if (Math.abs((actual as number) - (expected as number)) >= 0.000001) { - assert.fail( + throw new Error( `Number mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>` ); } break; case 'object': if ((actual === null) !== (expected === null)) { - assert.fail(`Null mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>`); + throw new Error(`Null mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>`); } else if (actual) { if (Array.isArray(actual) !== Array.isArray(expected)) { - assert.fail(`IsArray mismatch on hierarchy: ${path}`); + throw new Error(`IsArray mismatch on hierarchy: ${path}`); } else if (Array.isArray(actual) && Array.isArray(expected)) { const actualArray = TestPlatform.typedArrayAsUnknownArray(actual); const expectedArray = TestPlatform.typedArrayAsUnknownArray(expected); if (actualArray.length !== expectedArray.length) { - assert.fail( + throw new Error( `Array Length mismatch on hierarchy: ${path}, actual<${actualArray.length}> != expected<${expectedArray.length}>` ); } else { @@ -187,7 +185,7 @@ export class ComparisonHelpers { } } else if (expected instanceof Map) { if (!(actual instanceof Map)) { - assert.fail( + throw new Error( `Map mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>` ); } else { @@ -223,7 +221,7 @@ export class ComparisonHelpers { const actualKeyList = actualKeys.join(','); const expectedKeyList = expectedKeys.join(','); if (actualKeyList !== expectedKeyList) { - assert.fail( + throw new Error( `Object Keys mismatch on hierarchy: ${path}, actual<'${actualKeyList}'> != expected<'${expectedKeyList}'>` ); } else { @@ -245,14 +243,14 @@ export class ComparisonHelpers { break; case 'string': if ((actual as string) !== (expected as string)) { - assert.fail( + throw new Error( `String mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>` ); } break; case 'undefined': if (actual !== expected) { - assert.fail(`null mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>`); + throw new Error(`null mismatch on hierarchy: ${path}, actual<'${actual}'> != expected<'${expected}'>`); } break; } @@ -268,7 +266,6 @@ export class ComparisonHelpers { path: string, _ignoreKeys: string[] | null ): boolean { - assert.fail(`Cannot compare unknown object types on path ${path}`); - return false; + throw new Error(`Cannot compare unknown object types on path ${path}`); } } diff --git a/packages/alphatab/test/model/Font.test.ts b/packages/alphatab/test/model/Font.test.ts index d416c6968..b3b1760bf 100644 --- a/packages/alphatab/test/model/Font.test.ts +++ b/packages/alphatab/test/model/Font.test.ts @@ -1,15 +1,14 @@ +import { describe, expect, it } from 'vitest'; import { Font, FontStyle, FontWeight } from '@coderline/alphatab/model/Font'; -import { expect } from 'chai'; - describe('FontTests', () => { function parseText(text: string, expected: Font) { const font = Font.fromJson(text); - expect(font!.families.join(', ')).to.equal(expected.families.join(', ')); - expect(font!.isBold).to.equal(expected.isBold); - expect(font!.isItalic).to.equal(expected.isItalic); - expect(font!.size).to.equal(expected.size); - expect(font!.style).to.equal(expected.style); - expect(font!.weight).to.equal(expected.weight); + expect(font!.families.join(', ')).toBe(expected.families.join(', ')); + expect(font!.isBold).toBe(expected.isBold); + expect(font!.isItalic).toBe(expected.isItalic); + expect(font!.size).toBe(expected.size); + expect(font!.style).toBe(expected.style); + expect(font!.weight).toBe(expected.weight); } it('parses-full', () => { @@ -83,7 +82,7 @@ describe('FontTests', () => { }); function toCssStringTest(f: Font, expected: string) { - expect(f.toCssString()).to.equal(expected); + expect(f.toCssString()).toBe(expected); } it('css-string-tests', () => { diff --git a/packages/alphatab/test/model/HeaderFooterStyle.test.ts b/packages/alphatab/test/model/HeaderFooterStyle.test.ts index bdc78ea43..a9189ed70 100644 --- a/packages/alphatab/test/model/HeaderFooterStyle.test.ts +++ b/packages/alphatab/test/model/HeaderFooterStyle.test.ts @@ -1,42 +1,41 @@ +import { describe, expect, it } from 'vitest'; import { Score } from '@coderline/alphatab/model/Score'; import { HeaderFooterStyle } from '@coderline/alphatab/model/Score'; -import { expect } from 'chai'; - describe('HeaderFooterStyleTests', () => { it('buildTextSimple', () => { const score = new Score(); score.title = 'Title'; const style = new HeaderFooterStyle('%TITLE%'); - expect(style.buildText(score)).to.equal('Title'); + expect(style.buildText(score)).toBe('Title'); }); it('buildTextMultiple', () => { const score = new Score(); score.title = 'Title'; const style = new HeaderFooterStyle('%TITLE% %TITLE%'); - expect(style.buildText(score)).to.equal('Title Title'); + expect(style.buildText(score)).toBe('Title Title'); }); it('buildTextReuse', () => { const score = new Score(); score.title = 'Title'; const style = new HeaderFooterStyle('%TITLE% %TITLE%'); - expect(style.buildText(score)).to.equal('Title Title'); - expect(style.buildText(score)).to.equal('Title Title'); + expect(style.buildText(score)).toBe('Title Title'); + expect(style.buildText(score)).toBe('Title Title'); }); it('buildTextMultipleUnknown', () => { const score = new Score(); score.title = 'Title'; const style = new HeaderFooterStyle('%TITLE% %TITLE% %UNKNOWN% %INVALID%'); - expect(style.buildText(score)).to.equal('Title Title '); + expect(style.buildText(score)).toBe('Title Title '); }); it('buildTextEmptyIfMissingPlaceholderValue', () => { const score = new Score(); score.words = ''; const style = new HeaderFooterStyle('Words by %WORDS%'); - expect(style.buildText(score)).to.equal(''); + expect(style.buildText(score)).toBe(''); }); it('buildTextAll', () => { @@ -51,6 +50,6 @@ describe('HeaderFooterStyleTests', () => { score.copyright = 'Copyright'; const style = new HeaderFooterStyle('%TITLE% %SUBTITLE% %ARTIST% %ALBUM% %WORDS% %MUSIC% %TABBER% %COPYRIGHT%'); - expect(style.buildText(score)).to.equal('Title Subtitle Artist Album Words Music Tab Copyright'); + expect(style.buildText(score)).toBe('Title Subtitle Artist Album Words Music Tab Copyright'); }); }); diff --git a/packages/alphatab/test/model/JsonConverter.test.ts b/packages/alphatab/test/model/JsonConverter.test.ts index f68a942f0..0ee729c72 100644 --- a/packages/alphatab/test/model/JsonConverter.test.ts +++ b/packages/alphatab/test/model/JsonConverter.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { SettingsSerializer } from '@coderline/alphatab/generated/SettingsSerializer'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; @@ -8,7 +9,6 @@ import { JsonConverter } from '@coderline/alphatab/model/JsonConverter'; import type { Score } from '@coderline/alphatab/model/Score'; import { FingeringMode, NotationElement, NotationMode, TabRhythmMode } from '@coderline/alphatab/NotationSettings'; import { Settings } from '@coderline/alphatab/Settings'; -import { assert, expect } from 'chai'; import { TestPlatform } from 'test/TestPlatform'; import { ComparisonHelpers } from './ComparisonHelpers'; @@ -40,7 +40,7 @@ describe('JsonConverterTest', () => { null ); } catch (e) { - assert.fail(String(e)); + throw new Error(String(e)); } } @@ -170,14 +170,14 @@ describe('JsonConverterTest', () => { SettingsSerializer.fromJson(settings, raw); - expect(settings.core.enableLazyLoading).to.equal(false); - expect(settings.core.logLevel).to.equal(LogLevel.Error); - expect(settings.display.layoutMode).to.equal(LayoutMode.Horizontal); - expect(settings.display.scale).to.equal(5); - expect(settings.notation.rhythmMode).to.equal(TabRhythmMode.ShowWithBars); - expect(settings.display.resources.mainGlyphColor.r).to.equal(255); - expect(settings.display.resources.mainGlyphColor.g).to.equal(0); - expect(settings.display.resources.mainGlyphColor.b).to.equal(0); + expect(settings.core.enableLazyLoading).toBe(false); + expect(settings.core.logLevel).toBe(LogLevel.Error); + expect(settings.display.layoutMode).toBe(LayoutMode.Horizontal); + expect(settings.display.scale).toBe(5); + expect(settings.notation.rhythmMode).toBe(TabRhythmMode.ShowWithBars); + expect(settings.display.resources.mainGlyphColor.r).toBe(255); + expect(settings.display.resources.mainGlyphColor.g).toBe(0); + expect(settings.display.resources.mainGlyphColor.b).toBe(0); }); /*@target web*/ @@ -202,13 +202,13 @@ describe('JsonConverterTest', () => { SettingsSerializer.fromJson(settings, raw); - expect(settings.core.enableLazyLoading).to.equal(false); - expect(settings.core.logLevel).to.equal(LogLevel.Error); - expect(settings.display.layoutMode).to.equal(LayoutMode.Horizontal); - expect(settings.display.scale).to.equal(5); - expect(settings.notation.rhythmMode).to.equal(TabRhythmMode.ShowWithBars); - expect(settings.display.resources.mainGlyphColor.r).to.equal(255); - expect(settings.display.resources.mainGlyphColor.g).to.equal(0); - expect(settings.display.resources.mainGlyphColor.b).to.equal(0); + expect(settings.core.enableLazyLoading).toBe(false); + expect(settings.core.logLevel).toBe(LogLevel.Error); + expect(settings.display.layoutMode).toBe(LayoutMode.Horizontal); + expect(settings.display.scale).toBe(5); + expect(settings.notation.rhythmMode).toBe(TabRhythmMode.ShowWithBars); + expect(settings.display.resources.mainGlyphColor.r).toBe(255); + expect(settings.display.resources.mainGlyphColor.g).toBe(0); + expect(settings.display.resources.mainGlyphColor.b).toBe(0); }); }); diff --git a/packages/alphatab/test/model/KeySignatureUtils.test.ts b/packages/alphatab/test/model/KeySignatureUtils.test.ts index e0c5cc2ad..7464ecfa3 100644 --- a/packages/alphatab/test/model/KeySignatureUtils.test.ts +++ b/packages/alphatab/test/model/KeySignatureUtils.test.ts @@ -1,41 +1,40 @@ +import { describe, expect, it } from 'vitest'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { KeySignatureType } from '@coderline/alphatab/model/KeySignatureType'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; -import { expect } from 'chai'; - describe('KeySignatureUtilsTests', () => { it('sharp key signatures apply accidentals in F C G D A E B order', () => { const ksG = KeySignature.G; // 1 sharp -> F# - expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 3)).to.equal(1); // F - expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 0)).to.equal(0); // C - expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 6)).to.equal(0); // B + expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 3)).toBe(1); // F + expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 0)).toBe(0); // C + expect(ModelUtils.getKeySignatureAccidentalOffset(ksG, 6)).toBe(0); // B const ksD = KeySignature.D; // 2 sharps -> F#, C# - expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 3)).to.equal(1); // F - expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 0)).to.equal(1); // C - expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 4)).to.equal(0); // G + expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 3)).toBe(1); // F + expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 0)).toBe(1); // C + expect(ModelUtils.getKeySignatureAccidentalOffset(ksD, 4)).toBe(0); // G }); it('flat key signatures apply accidentals in B E A D G C F order', () => { const ksF = KeySignature.F; // 1 flat -> Bb - expect(ModelUtils.getKeySignatureAccidentalOffset(ksF, 6)).to.equal(-1); // B - expect(ModelUtils.getKeySignatureAccidentalOffset(ksF, 2)).to.equal(0); // E + expect(ModelUtils.getKeySignatureAccidentalOffset(ksF, 6)).toBe(-1); // B + expect(ModelUtils.getKeySignatureAccidentalOffset(ksF, 2)).toBe(0); // E const ksBb = KeySignature.Bb; // 2 flats -> Bb, Eb - expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 6)).to.equal(-1); // B - expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 2)).to.equal(-1); // E - expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 5)).to.equal(0); // A + expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 6)).toBe(-1); // B + expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 2)).toBe(-1); // E + expect(ModelUtils.getKeySignatureAccidentalOffset(ksBb, 5)).toBe(0); // A }); it('major tonic degree matches expected scale degree', () => { - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.C, KeySignatureType.Major)).to.equal(0); // C - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.G, KeySignatureType.Major)).to.equal(4); // G - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.Eb, KeySignatureType.Major)).to.equal(2); // Eb + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.C, KeySignatureType.Major)).toBe(0); // C + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.G, KeySignatureType.Major)).toBe(4); // G + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.Eb, KeySignatureType.Major)).toBe(2); // Eb }); it('minor tonic degree matches expected scale degree', () => { - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.C, KeySignatureType.Minor)).to.equal(5); // A - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.G, KeySignatureType.Minor)).to.equal(2); // E - expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.Bb, KeySignatureType.Minor)).to.equal(4); // G (relative minor) + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.C, KeySignatureType.Minor)).toBe(5); // A + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.G, KeySignatureType.Minor)).toBe(2); // E + expect(ModelUtils.getKeySignatureTonicDegree(KeySignature.Bb, KeySignatureType.Minor)).toBe(4); // G (relative minor) }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/Lyrics.test.ts b/packages/alphatab/test/model/Lyrics.test.ts index 5b26292b4..1ff0e1bff 100644 --- a/packages/alphatab/test/model/Lyrics.test.ts +++ b/packages/alphatab/test/model/Lyrics.test.ts @@ -1,11 +1,10 @@ +import { describe, expect, it } from 'vitest'; import { GpxImporter } from '@coderline/alphatab/importer/GpxImporter'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { Lyrics } from '@coderline/alphatab/model/Lyrics'; import type { Score } from '@coderline/alphatab/model/Score'; import { Settings } from '@coderline/alphatab/Settings'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - describe('LyricsTests', () => { async function loadLyricsTemplateFile(): Promise { const path: string = 'test-data/lyrics/template.gpx'; @@ -20,7 +19,7 @@ describe('LyricsTests', () => { const lyrics: Lyrics = new Lyrics(); lyrics.text = text; lyrics.finish(); - expect(lyrics.chunks.join(',')).to.equal(chunks.join(',')); + expect(lyrics.chunks.join(',')).toBe(chunks.join(',')); } it('apply-single-line-first-bar', async () => { @@ -29,14 +28,14 @@ describe('LyricsTests', () => { lyrics.text = 'AAA BBB CCC DDD EEE'; lyrics.startBar = 0; score.tracks[0].applyLyrics([lyrics]); - expect(1).to.equal(1); - expect('AAA').to.equal('AAA'); - expect('BBB').to.equal('BBB'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).to.not.be.ok; - expect('CCC').to.equal('CCC'); - expect(1).to.equal(1); - expect('DDD').to.equal('DDD'); - expect('EEE').to.equal('EEE'); + expect(1).toBe(1); + expect('AAA').toBe('AAA'); + expect('BBB').toBe('BBB'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).not.toBeTruthy(); + expect('CCC').toBe('CCC'); + expect(1).toBe(1); + expect('DDD').toBe('DDD'); + expect('EEE').toBe('EEE'); }); it('apply-single-line-bar-offset', async () => { @@ -46,14 +45,14 @@ describe('LyricsTests', () => { lyrics.startBar = 2; score.tracks[0].applyLyrics([lyrics]); - expect(1).to.equal(1); - expect('AAA').to.equal('AAA'); - expect('BBB').to.equal('BBB'); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).to.not.be.ok; - expect('CCC').to.equal('CCC'); - expect(1).to.equal(1); - expect('DDD').to.equal('DDD'); - expect('EEE').to.equal('EEE'); + expect(1).toBe(1); + expect('AAA').toBe('AAA'); + expect('BBB').toBe('BBB'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).not.toBeTruthy(); + expect('CCC').toBe('CCC'); + expect(1).toBe(1); + expect('DDD').toBe('DDD'); + expect('EEE').toBe('EEE'); }); it('apply-multi-line-first-bar', async () => { @@ -67,19 +66,19 @@ describe('LyricsTests', () => { lyrics2.startBar = 0; score.tracks[0].applyLyrics([lyrics1, lyrics2]); - expect(2).to.equal(2); - expect('AAA').to.equal('AAA'); - expect('111').to.equal('111'); - expect('BBB').to.equal('BBB'); - expect('222').to.equal('222'); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).to.not.be.ok; - expect('CCC').to.equal('CCC'); - expect('333').to.equal('333'); - expect(2).to.equal(2); - expect('DDD').to.equal('DDD'); - expect('444').to.equal('444'); - expect('EEE').to.equal('EEE'); - expect('555').to.equal('555'); + expect(2).toBe(2); + expect('AAA').toBe('AAA'); + expect('111').toBe('111'); + expect('BBB').toBe('BBB'); + expect('222').toBe('222'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).not.toBeTruthy(); + expect('CCC').toBe('CCC'); + expect('333').toBe('333'); + expect(2).toBe(2); + expect('DDD').toBe('DDD'); + expect('444').toBe('444'); + expect('EEE').toBe('EEE'); + expect('555').toBe('555'); }); it('apply-multi-line-bar-offset', async () => { @@ -93,22 +92,22 @@ describe('LyricsTests', () => { lyrics2.startBar = 1; score.tracks[0].applyLyrics([lyrics1, lyrics2]); - expect(2).to.equal(2); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].lyrics![0]).to.not.be.ok; - expect('111').to.equal('111'); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].lyrics![0]).to.not.be.ok; - expect('222').to.equal('222'); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].lyrics).to.not.be.ok; - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].lyrics![0]).to.not.be.ok; - expect('333').to.equal('333'); - expect(2).to.equal(2); - expect('AAA').to.equal('AAA'); - expect('444').to.equal('444'); - expect('BBB').to.equal('BBB'); - expect('555').to.equal('555'); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).to.not.be.ok; - expect('CCC').to.equal('CCC'); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].lyrics![1]).to.not.be.ok; + expect(2).toBe(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].lyrics![0]).not.toBeTruthy(); + expect('111').toBe('111'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].lyrics![0]).not.toBeTruthy(); + expect('222').toBe('222'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].lyrics).not.toBeTruthy(); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].lyrics![0]).not.toBeTruthy(); + expect('333').toBe('333'); + expect(2).toBe(2); + expect('AAA').toBe('AAA'); + expect('444').toBe('444'); + expect('BBB').toBe('BBB'); + expect('555').toBe('555'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).not.toBeTruthy(); + expect('CCC').toBe('CCC'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].lyrics![1]).not.toBeTruthy(); }); it('spaces', () => { diff --git a/packages/alphatab/test/model/ModelUtils.test.ts b/packages/alphatab/test/model/ModelUtils.test.ts index 7d78f5252..781d8598d 100644 --- a/packages/alphatab/test/model/ModelUtils.test.ts +++ b/packages/alphatab/test/model/ModelUtils.test.ts @@ -1,12 +1,11 @@ +import { describe, expect, it } from 'vitest'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; -import { expect } from 'chai'; - describe('ModelUtilsTests', () => { function trimTest(tex: string, expectedBars: number) { const score = ScoreLoader.loadAlphaTex(tex); - expect(score.masterBars.length).to.equal(expectedBars); + expect(score.masterBars.length).toBe(expectedBars); } it('trimEmptyBarsAtEndFullyEmpty', () => { @@ -41,10 +40,10 @@ describe('ModelUtilsTests', () => { describe('parseTuning', () => { function test(s:string, toneValue:number, accidental:NoteAccidentalMode, octave:number) { const result = ModelUtils.parseTuning(s); - expect(result).to.be.ok; - expect(result!.tone.noteValue).to.equal(toneValue); - expect(result!.tone.accidentalMode).to.equal(accidental); - expect(result!.octave).to.equal(octave); + expect(result).toBeTruthy(); + expect(result!.tone.noteValue).toBe(toneValue); + expect(result!.tone.accidentalMode).toBe(accidental); + expect(result!.octave).toBe(octave); } it('octave-c', () => test('C4', 0, NoteAccidentalMode.Default, 5)) diff --git a/packages/alphatab/test/model/NoteSteps.test.ts b/packages/alphatab/test/model/NoteSteps.test.ts index eed9961c5..c3f4c533c 100644 --- a/packages/alphatab/test/model/NoteSteps.test.ts +++ b/packages/alphatab/test/model/NoteSteps.test.ts @@ -1,18 +1,17 @@ +import { describe, expect, it } from 'vitest'; import { Clef } from '@coderline/alphatab/model/Clef'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; import { AccidentalHelper } from '@coderline/alphatab/rendering/utils/AccidentalHelper'; -import { expect } from 'chai'; - describe('NoteStepsTests', () => { it('calculates known steps for C4 in G2 and F4 clef', () => { const spelling = ModelUtils.resolveSpelling(KeySignature.C, 60, NoteAccidentalMode.Default); // C4 const stepsG2 = AccidentalHelper.calculateNoteSteps(Clef.G2, spelling); const stepsF4 = AccidentalHelper.calculateNoteSteps(Clef.F4, spelling); - expect(stepsG2).to.equal(10); - expect(stepsF4).to.equal(-2); + expect(stepsG2).toBe(10); + expect(stepsF4).toBe(-2); }); it('octave shift changes steps by 7', () => { @@ -20,7 +19,7 @@ describe('NoteStepsTests', () => { const spellingC5 = ModelUtils.resolveSpelling(KeySignature.C, 72, NoteAccidentalMode.Default); // C5 const stepsC4 = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingC4); const stepsC5 = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingC5); - expect(stepsC4 - stepsC5).to.equal(7); + expect(stepsC4 - stepsC5).toBe(7); }); it('adjacent diatonic degrees differ by one step', () => { @@ -28,7 +27,7 @@ describe('NoteStepsTests', () => { const spellingD4 = ModelUtils.resolveSpelling(KeySignature.C, 62, NoteAccidentalMode.Default); // D4 const stepsC4 = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingC4); const stepsD4 = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingD4); - expect(stepsC4 - stepsD4).to.equal(1); + expect(stepsC4 - stepsD4).toBe(1); }); it('same pitch with different spelling yields different steps', () => { @@ -39,6 +38,6 @@ describe('NoteStepsTests', () => { const stepsSharp = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingSharp); const stepsFlat = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingFlat); - expect(stepsSharp - stepsFlat).to.equal(1); // C# (degree 0) above Db (degree 1) + expect(stepsSharp - stepsFlat).toBe(1); // C# (degree 0) above Db (degree 1) }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/NoteStepsAdditional.test.ts b/packages/alphatab/test/model/NoteStepsAdditional.test.ts index d36c8e247..3a474efbb 100644 --- a/packages/alphatab/test/model/NoteStepsAdditional.test.ts +++ b/packages/alphatab/test/model/NoteStepsAdditional.test.ts @@ -1,16 +1,15 @@ +import { describe, expect, it } from 'vitest'; import { Clef } from '@coderline/alphatab/model/Clef'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; import { AccidentalHelper } from '@coderline/alphatab/rendering/utils/AccidentalHelper'; -import { expect } from 'chai'; - describe('NoteStepsAdditionalTests', () => { it('same pitch yields different steps across clefs', () => { const spelling = ModelUtils.resolveSpelling(KeySignature.C, 60, NoteAccidentalMode.Default); // C4 const stepsG2 = AccidentalHelper.calculateNoteSteps(Clef.G2, spelling); const stepsC4 = AccidentalHelper.calculateNoteSteps(Clef.C4, spelling); - expect(stepsG2 - stepsC4).to.equal(8); + expect(stepsG2 - stepsC4).toBe(8); }); it('enharmonic spelling changes steps (C# vs Db)', () => { @@ -21,6 +20,6 @@ describe('NoteStepsAdditionalTests', () => { const stepsSharp = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingSharp); const stepsFlat = AccidentalHelper.calculateNoteSteps(Clef.G2, spellingFlat); - expect(stepsSharp - stepsFlat).to.equal(1); + expect(stepsSharp - stepsFlat).toBe(1); }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/QuarterToneAccidentals.test.ts b/packages/alphatab/test/model/QuarterToneAccidentals.test.ts index 5ea377c87..9186e329b 100644 --- a/packages/alphatab/test/model/QuarterToneAccidentals.test.ts +++ b/packages/alphatab/test/model/QuarterToneAccidentals.test.ts @@ -1,16 +1,15 @@ +import { describe, expect, it } from 'vitest'; import { AccidentalType } from '@coderline/alphatab/model/AccidentalType'; import { KeySignature } from '@coderline/alphatab/model/KeySignature'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode'; -import { expect } from 'chai'; - describe('QuarterToneAccidentalsTests', () => { it('uses natural quarter tone when no key signature offset is required', () => { const ks = KeySignature.C; const noteValue = 60; // C4 const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.Default); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.Default, spelling, true, null); - expect(accidental).to.equal(AccidentalType.NaturalQuarterNoteUp); + expect(accidental).toBe(AccidentalType.NaturalQuarterNoteUp); }); it('uses sharp quarter tone for positive offset', () => { @@ -18,7 +17,7 @@ describe('QuarterToneAccidentalsTests', () => { const noteValue = 61; // C# const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceSharp); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceSharp, spelling, true, null); - expect(accidental).to.equal(AccidentalType.SharpQuarterNoteUp); + expect(accidental).toBe(AccidentalType.SharpQuarterNoteUp); }); it('uses flat quarter tone for negative offset', () => { @@ -26,6 +25,6 @@ describe('QuarterToneAccidentalsTests', () => { const noteValue = 61; // Db const spelling = ModelUtils.resolveSpelling(ks, noteValue, NoteAccidentalMode.ForceFlat); const accidental = ModelUtils.computeAccidentalForSpelling(ks, NoteAccidentalMode.ForceFlat, spelling, true, null); - expect(accidental).to.equal(AccidentalType.FlatQuarterNoteUp); + expect(accidental).toBe(AccidentalType.FlatQuarterNoteUp); }); }); \ No newline at end of file diff --git a/packages/alphatab/test/model/TuningParser.test.ts b/packages/alphatab/test/model/TuningParser.test.ts index e10b24982..b2cc62833 100644 --- a/packages/alphatab/test/model/TuningParser.test.ts +++ b/packages/alphatab/test/model/TuningParser.test.ts @@ -1,7 +1,6 @@ +import { describe, expect, it } from 'vitest'; import { Tuning } from '@coderline/alphatab/model/Tuning'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; -import { expect } from 'chai'; - describe('TuningParserTest', () => { it('standard', () => { const standard: Tuning = Tuning.getDefaultTuningFor(6)!; @@ -12,7 +11,7 @@ describe('TuningParserTest', () => { tuning[i] = ModelUtils.getTuningForText(tuningText[i]); tuningText2[i] = Tuning.getTextForTuning(tuning[i], true); } - expect(tuning.join(',')).to.equal(standard.tunings.join(',')); - expect(tuningText2.join(',')).to.equal(tuningText.join(',')); + expect(tuning.join(',')).toBe(standard.tunings.join(',')); + expect(tuningText2.join(',')).toBe(tuningText.join(',')); }); }); diff --git a/packages/alphatab/test/platform/Json.test.ts b/packages/alphatab/test/platform/Json.test.ts index cf494d3e7..1d8c75584 100644 --- a/packages/alphatab/test/platform/Json.test.ts +++ b/packages/alphatab/test/platform/Json.test.ts @@ -1,41 +1,40 @@ +import { describe, expect, it } from 'vitest'; import { Json } from '@coderline/alphatab/platform/Json'; -import { expect } from 'chai'; - describe('JsonTests', () => { it('quoteJsonStringEmpty', () => { - expect(Json.quoteJsonString('')).to.equal('""'); + expect(Json.quoteJsonString('')).toBe('""'); }); it('quoteJsonStringAscii', () => { - expect(Json.quoteJsonString('Test')).to.equal('"Test"'); + expect(Json.quoteJsonString('Test')).toBe('"Test"'); }); it('quoteJsonStringQuote', () => { - expect(Json.quoteJsonString('"')).to.equal('"\\""'); + expect(Json.quoteJsonString('"')).toBe('"\\""'); }); it('quoteJsonStringEscapes', () => { - expect(Json.quoteJsonString('\b')).to.equal('"\\b"'); - expect(Json.quoteJsonString('\t')).to.equal('"\\t"'); - expect(Json.quoteJsonString('\n')).to.equal('"\\n"'); + expect(Json.quoteJsonString('\b')).toBe('"\\b"'); + expect(Json.quoteJsonString('\t')).toBe('"\\t"'); + expect(Json.quoteJsonString('\n')).toBe('"\\n"'); /*@target web*/ - expect(Json.quoteJsonString('\f')).to.equal('"\\f"'); - expect(Json.quoteJsonString('\r')).to.equal('"\\r"'); - expect(Json.quoteJsonString('\\')).to.equal('"\\\\"'); + expect(Json.quoteJsonString('\f')).toBe('"\\f"'); + expect(Json.quoteJsonString('\r')).toBe('"\\r"'); + expect(Json.quoteJsonString('\\')).toBe('"\\\\"'); }); it('quoteJsonStringNonReadable', () => { - expect(Json.quoteJsonString('\u001B\u001B')).to.equal('"\\u001b\\u001b"'); + expect(Json.quoteJsonString('\u001B\u001B')).toBe('"\\u001b\\u001b"'); }); it('quoteJsonStringSurrogates', () => { - expect(Json.quoteJsonString('\udc00\udc00')).to.equal('"\\udc00\\udc00"'); + expect(Json.quoteJsonString('\udc00\udc00')).toBe('"\\udc00\\udc00"'); }); it('quoteJsonStringSurrogatePair', () => { // cat emoji 😸 - expect(Json.quoteJsonString('\uD83D\uDE38')).to.equal('"😸"'); + expect(Json.quoteJsonString('\uD83D\uDE38')).toBe('"😸"'); // hand emoji (color adjusted) 🤘🏻 - expect(Json.quoteJsonString('\uD83E\uDD18\uD83C\uDFFB')).to.equal('"🤘🏻"'); + expect(Json.quoteJsonString('\uD83E\uDD18\uD83C\uDFFB')).toBe('"🤘🏻"'); }); }); diff --git a/packages/alphatab/test/rendering/BoundsLookupPartialUpdate.test.ts b/packages/alphatab/test/rendering/BoundsLookupPartialUpdate.test.ts new file mode 100644 index 000000000..67ed11b62 --- /dev/null +++ b/packages/alphatab/test/rendering/BoundsLookupPartialUpdate.test.ts @@ -0,0 +1,191 @@ +import { describe, expect, it } from 'vitest'; +import { Bar } from '@coderline/alphatab/model/Bar'; +import { Beat } from '@coderline/alphatab/model/Beat'; +import { MasterBar } from '@coderline/alphatab/model/MasterBar'; +import { Score } from '@coderline/alphatab/model/Score'; +import { Staff } from '@coderline/alphatab/model/Staff'; +import { Track } from '@coderline/alphatab/model/Track'; +import { Voice } from '@coderline/alphatab/model/Voice'; +import { BarBounds } from '@coderline/alphatab/rendering/utils/BarBounds'; +import { BeatBounds } from '@coderline/alphatab/rendering/utils/BeatBounds'; +import { Bounds } from '@coderline/alphatab/rendering/utils/Bounds'; +import { BoundsLookup } from '@coderline/alphatab/rendering/utils/BoundsLookup'; +import { MasterBarBounds } from '@coderline/alphatab/rendering/utils/MasterBarBounds'; +import { StaffSystemBounds } from '@coderline/alphatab/rendering/utils/StaffSystemBounds'; +// Covers the partial-render preservation path: ScoreRenderer.render reuses an existing +// BoundsLookup when renderHints.firstChangedMasterBar is set, VerticalLayoutBase prunes the +// changed range via BoundsLookup.clearFromMasterBar, and BoundsLookup.finish must remain +// safe to call again without double-scaling the preserved systems. +describe('BoundsLookupPartialUpdate', () => { + function makeBounds(x: number, y: number, w: number, h: number): Bounds { + const b = new Bounds(); + b.x = x; + b.y = y; + b.w = w; + b.h = h; + return b; + } + + function buildScore(barCount: number, beatsPerBar: number): Score { + const score = new Score(); + const track = new Track(); + const staff = new Staff(); + track.addStaff(staff); + score.addTrack(track); + for (let i = 0; i < barCount; i++) { + const masterBar = new MasterBar(); + score.addMasterBar(masterBar); + const bar = new Bar(); + staff.addBar(bar); + const voice = new Voice(); + bar.addVoice(voice); + for (let b = 0; b < beatsPerBar; b++) { + voice.addBeat(new Beat()); + } + } + return score; + } + + function populateLookup(score: Score, barsPerSystem: number): BoundsLookup { + const lookup = new BoundsLookup(); + const staff = score.tracks[0].staves[0]; + let systemStart = 0; + while (systemStart < staff.bars.length) { + const system = new StaffSystemBounds(); + system.visualBounds = makeBounds(0, systemStart * 200, 1000, 100); + system.realBounds = makeBounds(0, systemStart * 200, 1000, 200); + lookup.addStaffSystem(system); + + const end = Math.min(systemStart + barsPerSystem, staff.bars.length); + for (let i = systemStart; i < end; i++) { + const bar = staff.bars[i]; + const mb = new MasterBarBounds(); + mb.index = i; + mb.visualBounds = makeBounds(i * 100, systemStart * 200, 100, 100); + mb.realBounds = makeBounds(i * 100, systemStart * 200, 100, 200); + mb.lineAlignedBounds = makeBounds(i * 100, systemStart * 200, 100, 100); + lookup.addMasterBar(mb); + + const bb = new BarBounds(); + bb.bar = bar; + bb.visualBounds = makeBounds(i * 100, systemStart * 200, 100, 100); + bb.realBounds = makeBounds(i * 100, systemStart * 200, 100, 200); + mb.addBar(bb); + + for (const beat of bar.voices[0].beats) { + const beatBounds = new BeatBounds(); + beatBounds.beat = beat; + beatBounds.visualBounds = makeBounds(i * 100, systemStart * 200, 20, 100); + beatBounds.realBounds = makeBounds(i * 100, systemStart * 200, 20, 200); + bb.addBeat(beatBounds); + } + } + systemStart = end; + } + return lookup; + } + + it('StaffSystemBounds.finish is idempotent across multiple calls', () => { + const system = new StaffSystemBounds(); + system.visualBounds = makeBounds(10, 20, 100, 50); + system.realBounds = makeBounds(10, 20, 100, 80); + + expect(system.isFinished).toBe(false); + + system.finish(2); + + expect(system.isFinished).toBe(true); + expect(system.visualBounds.x).toBe(20); + expect(system.realBounds.w).toBe(200); + + // second finish() call must be a no-op - otherwise preserved systems get double-scaled + system.finish(2); + + expect(system.visualBounds.x).toBe(20); + expect(system.realBounds.w).toBe(200); + }); + + it('BoundsLookup.resetForPartialUpdate reopens the lookup for registrations', () => { + const lookup = new BoundsLookup(); + lookup.finish(1); + + expect(lookup.isFinished).toBe(true); + + lookup.resetForPartialUpdate(); + + expect(lookup.isFinished).toBe(false); + }); + + it('BoundsLookup.clearFromMasterBar keeps preserved entries and drops the changed range', () => { + // 2 systems, 5 bars each (indexes 0-4 in system 0, 5-9 in system 1), 2 beats per bar + const score = buildScore(10, 2); + const lookup = populateLookup(score, 5); + + expect(lookup.staffSystems.length).toBe(2); + expect(lookup.findMasterBarByIndex(0)).not.toBe(null); + expect(lookup.findMasterBarByIndex(9)).not.toBe(null); + + // partial render targeting system 1 - clear from the first bar of system 1 + lookup.clearFromMasterBar(5); + + // system 0 survives with its bars intact + expect(lookup.staffSystems.length).toBe(1); + expect(lookup.findMasterBarByIndex(0)).not.toBe(null); + expect(lookup.findMasterBarByIndex(4)).not.toBe(null); + + // everything from bar 5 onward is gone + expect(lookup.findMasterBarByIndex(5)).toBe(null); + expect(lookup.findMasterBarByIndex(9)).toBe(null); + + // beat lookup: beats in preserved bars still findable, beats in cleared bars are not + const preservedBeat = score.tracks[0].staves[0].bars[2].voices[0].beats[0]; + const clearedBeat = score.tracks[0].staves[0].bars[7].voices[0].beats[0]; + expect(lookup.findBeat(preservedBeat)).not.toBe(null); + expect(lookup.findBeat(clearedBeat)).toBe(null); + }); + + it('clearFromMasterBar + re-register preserves already-scaled bounds and scales only new ones', () => { + const score = buildScore(10, 2); + const lookup = populateLookup(score, 5); + + // first render: finish once to lock preserved systems at their scaled coords + lookup.finish(2); + const preservedSystem0 = lookup.staffSystems[0]; + const preservedRealX = preservedSystem0.realBounds.x; + const preservedRealW = preservedSystem0.realBounds.w; + + // simulate a partial render: reset, clear the changed range, register new bounds for it + lookup.resetForPartialUpdate(); + lookup.clearFromMasterBar(5); + + const staff = score.tracks[0].staves[0]; + const newSystem = new StaffSystemBounds(); + newSystem.visualBounds = makeBounds(0, 200, 1000, 100); + newSystem.realBounds = makeBounds(0, 200, 1000, 200); + lookup.addStaffSystem(newSystem); + for (let i = 5; i < 10; i++) { + const mb = new MasterBarBounds(); + mb.index = i; + mb.visualBounds = makeBounds(i * 100, 200, 100, 100); + mb.realBounds = makeBounds(i * 100, 200, 100, 200); + mb.lineAlignedBounds = makeBounds(i * 100, 200, 100, 100); + lookup.addMasterBar(mb); + + const bb = new BarBounds(); + bb.bar = staff.bars[i]; + bb.visualBounds = makeBounds(i * 100, 200, 100, 100); + bb.realBounds = makeBounds(i * 100, 200, 100, 200); + mb.addBar(bb); + } + + // finish again at the same scale - preserved system must NOT be re-scaled + lookup.finish(2); + + expect(preservedSystem0.realBounds.x).toBe(preservedRealX); + expect(preservedSystem0.realBounds.w).toBe(preservedRealW); + + // the newly registered system gets scaled exactly once + expect(newSystem.isFinished).toBe(true); + expect(newSystem.realBounds.w).toBe(2000); + }); +}); diff --git a/packages/alphatab/test/visualTests/TestUiFacade.ts b/packages/alphatab/test/visualTests/TestUiFacade.ts index 1b8ad03dd..c9c63fbaf 100644 --- a/packages/alphatab/test/visualTests/TestUiFacade.ts +++ b/packages/alphatab/test/visualTests/TestUiFacade.ts @@ -8,16 +8,18 @@ import { import { Settings } from '@coderline/alphatab/Settings'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { Score } from '@coderline/alphatab/model/Score'; -import type { Cursors } from '@coderline/alphatab/platform/Cursors'; +import { Cursors } from '@coderline/alphatab/platform/Cursors'; import type { IContainer } from '@coderline/alphatab/platform/IContainer'; import type { IMouseEventArgs } from '@coderline/alphatab/platform/IMouseEventArgs'; import type { IUiFacade } from '@coderline/alphatab/platform/IUiFacade'; import type { IScoreRenderer } from '@coderline/alphatab/rendering/IScoreRenderer'; import type { RenderFinishedEventArgs } from '@coderline/alphatab/rendering/RenderFinishedEventArgs'; import { Bounds } from '@coderline/alphatab/rendering/utils/Bounds'; +import { AlphaSynth } from '@coderline/alphatab/synth/AlphaSynth'; import type { IAlphaSynth } from '@coderline/alphatab/synth/IAlphaSynth'; import type { IAudioExporterWorker } from '@coderline/alphatab/synth/IAudioExporter'; import { TestPlatform } from 'test/TestPlatform'; +import { TestOutput } from 'test/audio/TestOutput'; /** * @internal @@ -222,11 +224,18 @@ export class TestUiFacade implements IUiFacade { } public createWorkerPlayer(): IAlphaSynth | null { - throw new Error('Not supported'); + return new AlphaSynth(new TestOutput(), 500); } + private _cursors?: Cursors; public createCursors(): Cursors | null { - return null; + this._cursors = this._cursors ?? new Cursors( + new TestUiContainer(), + new TestUiContainer(), + new TestUiContainer(), + new TestUiContainer() + ); + return this._cursors; } public destroyCursors(): void {} @@ -240,7 +249,7 @@ export class TestUiFacade implements IUiFacade { public highlightElements(_groupId: string, _masterBarIndex: number): void {} public createSelectionElement(): IContainer | null { - return null; + return new TestUiContainer(); } public getScrollContainer(): IContainer { diff --git a/packages/alphatab/test/visualTests/features/BoundsLookup.test.ts b/packages/alphatab/test/visualTests/features/BoundsLookup.test.ts index 665803582..667cdcbe2 100644 --- a/packages/alphatab/test/visualTests/features/BoundsLookup.test.ts +++ b/packages/alphatab/test/visualTests/features/BoundsLookup.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { Color } from '@coderline/alphatab/model/Color'; import { VisualTestHelper, VisualTestOptions } from '../VisualTestHelper'; import { AlphaSkiaCanvas } from '@coderline/alphaskia'; diff --git a/packages/alphatab/test/visualTests/features/EffectsAndAnnotations.test.ts b/packages/alphatab/test/visualTests/features/EffectsAndAnnotations.test.ts index 1d096492a..85224ac11 100644 --- a/packages/alphatab/test/visualTests/features/EffectsAndAnnotations.test.ts +++ b/packages/alphatab/test/visualTests/features/EffectsAndAnnotations.test.ts @@ -1,10 +1,11 @@ import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; +import { NotationElement } from '@coderline/alphatab/NotationSettings'; import { BeatBarreEffectInfo } from '@coderline/alphatab/rendering/effects/BeatBarreEffectInfo'; import { Settings } from '@coderline/alphatab/Settings'; -import { expect } from 'chai'; import { TestPlatform } from 'test/TestPlatform'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from 'test/visualTests/VisualTestHelper'; +import { describe, expect, it } from 'vitest'; describe('EffectsAndAnnotationsTests', () => { it('markers', async () => { @@ -191,7 +192,7 @@ describe('EffectsAndAnnotationsTests', () => { ); score.stylesheet.hideDynamics = true; - expect(score.tracks[0].staves[0].displayTranspositionPitch).to.equal(0); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toBe(0); await VisualTestHelper.runVisualTestFull( new VisualTestOptions( @@ -219,36 +220,36 @@ describe('EffectsAndAnnotationsTests', () => { }); it('roman-numbers', () => { - expect(BeatBarreEffectInfo.toRoman(0)).to.equal(''); - expect(BeatBarreEffectInfo.toRoman(1)).to.equal('I'); - expect(BeatBarreEffectInfo.toRoman(2)).to.equal('II'); - expect(BeatBarreEffectInfo.toRoman(3)).to.equal('III'); - expect(BeatBarreEffectInfo.toRoman(4)).to.equal('IV'); - expect(BeatBarreEffectInfo.toRoman(5)).to.equal('V'); - expect(BeatBarreEffectInfo.toRoman(6)).to.equal('VI'); - expect(BeatBarreEffectInfo.toRoman(7)).to.equal('VII'); - expect(BeatBarreEffectInfo.toRoman(8)).to.equal('VIII'); - expect(BeatBarreEffectInfo.toRoman(9)).to.equal('IX'); - expect(BeatBarreEffectInfo.toRoman(10)).to.equal('X'); - expect(BeatBarreEffectInfo.toRoman(11)).to.equal('XI'); - expect(BeatBarreEffectInfo.toRoman(12)).to.equal('XII'); - expect(BeatBarreEffectInfo.toRoman(13)).to.equal('XIII'); - expect(BeatBarreEffectInfo.toRoman(14)).to.equal('XIV'); - expect(BeatBarreEffectInfo.toRoman(15)).to.equal('XV'); - expect(BeatBarreEffectInfo.toRoman(16)).to.equal('XVI'); - expect(BeatBarreEffectInfo.toRoman(17)).to.equal('XVII'); - expect(BeatBarreEffectInfo.toRoman(18)).to.equal('XVIII'); - expect(BeatBarreEffectInfo.toRoman(19)).to.equal('XIX'); - expect(BeatBarreEffectInfo.toRoman(20)).to.equal('XX'); - expect(BeatBarreEffectInfo.toRoman(21)).to.equal('XXI'); - expect(BeatBarreEffectInfo.toRoman(22)).to.equal('XXII'); - expect(BeatBarreEffectInfo.toRoman(23)).to.equal('XXIII'); - expect(BeatBarreEffectInfo.toRoman(24)).to.equal('XXIV'); - expect(BeatBarreEffectInfo.toRoman(25)).to.equal('XXV'); - expect(BeatBarreEffectInfo.toRoman(26)).to.equal('XXVI'); - expect(BeatBarreEffectInfo.toRoman(27)).to.equal('XXVII'); - expect(BeatBarreEffectInfo.toRoman(28)).to.equal('XXVIII'); - expect(BeatBarreEffectInfo.toRoman(29)).to.equal('XXIX'); + expect(BeatBarreEffectInfo.toRoman(0)).toBe(''); + expect(BeatBarreEffectInfo.toRoman(1)).toBe('I'); + expect(BeatBarreEffectInfo.toRoman(2)).toBe('II'); + expect(BeatBarreEffectInfo.toRoman(3)).toBe('III'); + expect(BeatBarreEffectInfo.toRoman(4)).toBe('IV'); + expect(BeatBarreEffectInfo.toRoman(5)).toBe('V'); + expect(BeatBarreEffectInfo.toRoman(6)).toBe('VI'); + expect(BeatBarreEffectInfo.toRoman(7)).toBe('VII'); + expect(BeatBarreEffectInfo.toRoman(8)).toBe('VIII'); + expect(BeatBarreEffectInfo.toRoman(9)).toBe('IX'); + expect(BeatBarreEffectInfo.toRoman(10)).toBe('X'); + expect(BeatBarreEffectInfo.toRoman(11)).toBe('XI'); + expect(BeatBarreEffectInfo.toRoman(12)).toBe('XII'); + expect(BeatBarreEffectInfo.toRoman(13)).toBe('XIII'); + expect(BeatBarreEffectInfo.toRoman(14)).toBe('XIV'); + expect(BeatBarreEffectInfo.toRoman(15)).toBe('XV'); + expect(BeatBarreEffectInfo.toRoman(16)).toBe('XVI'); + expect(BeatBarreEffectInfo.toRoman(17)).toBe('XVII'); + expect(BeatBarreEffectInfo.toRoman(18)).toBe('XVIII'); + expect(BeatBarreEffectInfo.toRoman(19)).toBe('XIX'); + expect(BeatBarreEffectInfo.toRoman(20)).toBe('XX'); + expect(BeatBarreEffectInfo.toRoman(21)).toBe('XXI'); + expect(BeatBarreEffectInfo.toRoman(22)).toBe('XXII'); + expect(BeatBarreEffectInfo.toRoman(23)).toBe('XXIII'); + expect(BeatBarreEffectInfo.toRoman(24)).toBe('XXIV'); + expect(BeatBarreEffectInfo.toRoman(25)).toBe('XXV'); + expect(BeatBarreEffectInfo.toRoman(26)).toBe('XXVI'); + expect(BeatBarreEffectInfo.toRoman(27)).toBe('XXVII'); + expect(BeatBarreEffectInfo.toRoman(28)).toBe('XXVIII'); + expect(BeatBarreEffectInfo.toRoman(29)).toBe('XXIX'); }); it('barre', async () => { @@ -573,4 +574,66 @@ describe('EffectsAndAnnotationsTests', () => { ); }); }); + + describe('hopo-arcs', () => { + async function test(test: string, tex: string) { + await VisualTestHelper.runVisualTestTex( + tex, + `test-data/visual-tests/effects-and-annotations/hopo-arcs-${test}.png` + ); + } + + it('at1', async () => await test('at1', ':4 5.3{h} 7.3 r r')); + it('at2', async () => await test('at2', ':4 7.3{h} 5.3 r r')); + it('at3', async () => await test('at3', ':4 5.3{h} 7.3 7.3{h} 5.3')); + it('at4', async () => await test('at4', ':4 5.3{h} 7.3 8.4{h} 5.4')); + it('at5', async () => await test('at5', ':4 5.3{h} 7.3{h} 5.3 r')); + it('at6', async () => await test('at6', ':8 5.3{h} 7.3{h} 5.3{h} 7.3 r r r r')); + it('at7', async () => await test('at7', ':4 5.3{sl} 7.3 r r')); + it('at8', async () => await test('at8', ':4 5.3 7.3 5.3 7.3')); + it('at9', async () => await test('at9', ':4 (5.3{h} 5.4) (7.3 7.4) r r')); + it('at10', async () => await test('at10', ':4 (5.3 5.4{h}) (7.3 7.4) r r')); + it('at11', async () => await test('at11', ':4 (5.3{h} 5.4{h}) (7.3 7.4) r r')); + it('at12', async () => await test('at12', ':4 (5.3{h} 7.4{h}) (7.3 5.4) r r')); + it('at13', async () => await test('at13', ':4 (5.3{h} 7.4{h}) (7.3{h} 5.4{h}) (5.3 7.4) r')); + it('at14', async () => await test('at14', ':4 5.3 {h} 7.3{h} 5.3 | 5.4 {h} 7.4{h} 5.4')); + + // Pure descending pull-off chain + it('pull-off-chain', async () => await test('pull-off-chain', ':4 9.3{h} 7.3{h} 5.3 r')); + + // Mixed hammer-on + legato slide in one chain — the core + // "combined effects" case that motivated the redesign. Both + // labels (H, sl.) appear above the single arc. + it('mixed-h-slide', async () => await test('mixed-h-slide', ':4 5.3{h} 7.3{sl} 9.3 r')); + it('mixed-slide-h-p', async () => await test('mixed-slide-h-p', ':4 5.3{sl} 7.3{h} 9.3{h} 7.3')); + + // Chain that swings up then down: H then P inside one arc + it('asc-then-desc', async () => await test('asc-then-desc', ':4 5.3{h} 7.3{h} 9.3{h} 7.3{h} 5.3')); + + // Three-note chord with H/P chain on the upper string + it('chord-with-chain', async () => + await test('chord-with-chain', ':4 (5.3{h} 5.4 5.5) (7.3{h} 7.4 7.5) (5.3 5.4 5.5) r')); + + // Score-only — confirms ScoreSlurGlyph paints labels even + // without the tab staff present. + it('score-only', async () => + await test('score-only', '\\track "T" \\staff {score} :4 5.3{h} 7.3{h} 5.3{sl} 7.3')); + + // Tab-only — confirms TabSlurGlyph paints labels in isolation. + it('tab-only', async () => + await test('tab-only', '\\track "T" \\staff {tabs} :4 5.3{h} 7.3{h} 5.3{sl} 7.3')); + + // Labels disabled via NotationSettings — arcs still render but + // without H/P/sl. text above them. + it('labels-disabled', async () => { + const settings = new Settings(); + settings.notation.elements.set(NotationElement.EffectHammerOnPullOffText, false); + settings.notation.elements.set(NotationElement.EffectSlideText, false); + await VisualTestHelper.runVisualTestTex( + ':4 5.3{h} 7.3{h} 5.3{sl} 7.3', + 'test-data/visual-tests/effects-and-annotations/hopo-arcs-labels-disabled.png', + settings + ); + }); + }); }); diff --git a/packages/alphatab/test/visualTests/features/General.test.ts b/packages/alphatab/test/visualTests/features/General.test.ts index 9c46858c7..6b5b934ed 100644 --- a/packages/alphatab/test/visualTests/features/General.test.ts +++ b/packages/alphatab/test/visualTests/features/General.test.ts @@ -1,8 +1,8 @@ +import { describe, expect, it } from 'vitest'; import { StaveProfile } from '@coderline/alphatab/StaveProfile'; import { Settings } from '@coderline/alphatab/Settings'; import { VisualTestHelper } from 'test/visualTests/VisualTestHelper'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; import { Logger } from '@coderline/alphatab/Logger'; import { BarStyle, BarSubElement } from '@coderline/alphatab/model/Bar'; import { BeatStyle, BeatSubElement } from '@coderline/alphatab/model/Beat'; @@ -172,7 +172,7 @@ describe('GeneralTests', () => { const coloredDuration = coloredEnd - coloredStart; const defaultDuration = defaultEnd - defaultStart; - expect(coloredDuration - defaultDuration).to.be.lessThan(120); + expect(coloredDuration - defaultDuration).toBeLessThan(120); Logger.info('Test-color-performance', 'Colored', i, coloredDuration); Logger.info('Test-color-performance', 'Default', i, defaultDuration); diff --git a/packages/alphatab/test/visualTests/features/GuitarTabs.test.ts b/packages/alphatab/test/visualTests/features/GuitarTabs.test.ts index dcf7020bd..1617b2904 100644 --- a/packages/alphatab/test/visualTests/features/GuitarTabs.test.ts +++ b/packages/alphatab/test/visualTests/features/GuitarTabs.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { StaveProfile } from '@coderline/alphatab/StaveProfile'; import { TabRhythmMode } from '@coderline/alphatab/NotationSettings'; import { Settings } from '@coderline/alphatab/Settings'; diff --git a/packages/alphatab/test/visualTests/features/Layout.test.ts b/packages/alphatab/test/visualTests/features/Layout.test.ts index 262fa3573..c2a12bac7 100644 --- a/packages/alphatab/test/visualTests/features/Layout.test.ts +++ b/packages/alphatab/test/visualTests/features/Layout.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from 'test/visualTests/VisualTestHelper'; diff --git a/packages/alphatab/test/visualTests/features/MultiVoice.test.ts b/packages/alphatab/test/visualTests/features/MultiVoice.test.ts index 2c21ece9b..418fee55a 100644 --- a/packages/alphatab/test/visualTests/features/MultiVoice.test.ts +++ b/packages/alphatab/test/visualTests/features/MultiVoice.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; import { TestPlatform } from 'test/TestPlatform'; diff --git a/packages/alphatab/test/visualTests/features/MusicNotation.test.ts b/packages/alphatab/test/visualTests/features/MusicNotation.test.ts index 2339a1a3d..9016987a2 100644 --- a/packages/alphatab/test/visualTests/features/MusicNotation.test.ts +++ b/packages/alphatab/test/visualTests/features/MusicNotation.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { NotationElement } from '@coderline/alphatab/NotationSettings'; import { Settings } from '@coderline/alphatab/Settings'; diff --git a/packages/alphatab/test/visualTests/features/NotationElements.test.ts b/packages/alphatab/test/visualTests/features/NotationElements.test.ts index adcd10ba3..a80dcf7b2 100644 --- a/packages/alphatab/test/visualTests/features/NotationElements.test.ts +++ b/packages/alphatab/test/visualTests/features/NotationElements.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; import { VisualTestHelper, VisualTestOptions } from 'test/visualTests/VisualTestHelper'; diff --git a/packages/alphatab/test/visualTests/features/NotationLegend.test.ts b/packages/alphatab/test/visualTests/features/NotationLegend.test.ts index 331429e86..035f35958 100644 --- a/packages/alphatab/test/visualTests/features/NotationLegend.test.ts +++ b/packages/alphatab/test/visualTests/features/NotationLegend.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from 'test/visualTests/VisualTestHelper'; diff --git a/packages/alphatab/test/visualTests/features/PrefixOverhead.test.ts b/packages/alphatab/test/visualTests/features/PrefixOverhead.test.ts new file mode 100644 index 000000000..a517ccbe7 --- /dev/null +++ b/packages/alphatab/test/visualTests/features/PrefixOverhead.test.ts @@ -0,0 +1,101 @@ +import { describe, it } from 'vitest'; +import { SystemsLayoutMode } from '@coderline/alphatab/DisplaySettings'; +import { LayoutMode } from '@coderline/alphatab/LayoutMode'; +import { Settings } from '@coderline/alphatab/Settings'; +import { VisualTestHelper } from 'test/visualTests/VisualTestHelper'; + +/** + * Regression coverage for the fixed-overhead layout fix: prefix glyphs (clef, key sig, time sig, barlines) + * are now treated as fixed overhead and the remaining staff width is distributed across bars by a per-bar + * weight, matching Behind Bars / Dorico / Finale / Sibelius / MuseScore / Guitar Pro. + * + * Weight source by mode: + * - Parchment and Page with UseModelLayout -> bar.displayScale (default 1 == unset, matches GP) + * - Page with Automatic -> natural content width (displayScale ignored) + */ +describe('PrefixOverheadTests', () => { + it('parchment-prefix-system-start', async () => { + // Bar 1 carries the system-start prefix (clef + 3/4 time sig + G-major key sig). + // Bars 2-3 are plain. Expectation: bar 1 is visibly wider than bars 2-3. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\ks G \\ts 3 4 + :4 c4 c4 c4 | c4 c4 c4 | c4 c4 c4 + `, + 'test-data/visual-tests/prefix-overhead/parchment-prefix-system-start.png', + settings + ); + }); + + it('parchment-prefix-midline-keysig-change', async () => { + // Bar 3 carries a mid-line key-signature change (C-major -> D-major). Expectation: bar 3 is + // visibly wider than bars 2 and 4 because the new KS glyphs take fixed overhead. + // defaultSystemsLayout forces all four bars onto a single system so the prefix/no-prefix + // width comparison is visible at a glance rather than split across wrapped lines. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 4 } + \\ts 4 4 + :4 c4 c4 c4 c4 | c4 c4 c4 c4 | \\ks D c4 c4 c4 c4 | c4 c4 c4 c4 + `, + 'test-data/visual-tests/prefix-overhead/parchment-prefix-midline-keysig-change.png', + settings + ); + }); + + it('parchment-authored-scale-with-prefix', async () => { + // Mirror of the Guitar Pro "file 2" test that drove the design: bar 1 has a system-start prefix + // (clef + 4/4) and displayScale=1, bar 2 has no prefix and displayScale=2. Expectation: bar 2's + // content area is roughly twice as wide as bar 1's content area, while bar 1 retains its full + // prefix overhead on top of its content share. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\ts 4 4 + \\scale 1 :4 c4 c4 c4 c4 | \\scale 2 c4 c4 c4 c4 + `, + 'test-data/visual-tests/prefix-overhead/parchment-authored-scale-with-prefix.png', + settings + ); + }); + + it('page-automatic-prefix-system-start', async () => { + // Same score as parchment-prefix-system-start, rendered in Page layout with SystemsLayoutMode.Automatic. + // Expectation: bar 1 is still visibly wider than bars 2-3 because fixed overhead applies in both + // modes. The weight source (content-width vs displayScale) differs, but the prefix bucket does not. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + settings.display.systemsLayoutMode = SystemsLayoutMode.Automatic; + await VisualTestHelper.runVisualTestTex( + ` + \\ks G \\ts 3 4 + :4 c4 c4 c4 | c4 c4 c4 | c4 c4 c4 + `, + 'test-data/visual-tests/prefix-overhead/page-automatic-prefix-system-start.png', + settings + ); + }); + + it('page-automatic-ignores-displayscale', async () => { + // Three bars with identical rhythmic content but middle bar authored to displayScale=2. In Page + // with SystemsLayoutMode.Automatic, displayScale must be ignored: the three bars should therefore + // receive equal content-area widths, driven by their equal natural content width. This guards + // against regressions that would make Page silently honour authored scales. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + settings.display.systemsLayoutMode = SystemsLayoutMode.Automatic; + await VisualTestHelper.runVisualTestTex( + ` + \\ts 4 4 + :4 c4 c4 c4 c4 | \\scale 2 c4 c4 c4 c4 | c4 c4 c4 c4 + `, + 'test-data/visual-tests/prefix-overhead/page-automatic-ignores-displayscale.png', + settings + ); + }); +}); diff --git a/packages/alphatab/test/visualTests/features/SpecialNotes.test.ts b/packages/alphatab/test/visualTests/features/SpecialNotes.test.ts index 389027d72..8a50c2c4c 100644 --- a/packages/alphatab/test/visualTests/features/SpecialNotes.test.ts +++ b/packages/alphatab/test/visualTests/features/SpecialNotes.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from 'test/visualTests/VisualTestHelper'; describe('SpecialNotesTests', () => { diff --git a/packages/alphatab/test/visualTests/features/SpecialTracks.test.ts b/packages/alphatab/test/visualTests/features/SpecialTracks.test.ts index 448c30d17..00c7e6a6c 100644 --- a/packages/alphatab/test/visualTests/features/SpecialTracks.test.ts +++ b/packages/alphatab/test/visualTests/features/SpecialTracks.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { VisualTestHelper } from 'test/visualTests/VisualTestHelper'; diff --git a/packages/alphatab/test/visualTests/features/SystemSpacing.test.ts b/packages/alphatab/test/visualTests/features/SystemSpacing.test.ts new file mode 100644 index 000000000..5a562f92a --- /dev/null +++ b/packages/alphatab/test/visualTests/features/SystemSpacing.test.ts @@ -0,0 +1,234 @@ +import { describe, it } from 'vitest'; +import { LayoutMode } from '@coderline/alphatab/LayoutMode'; +import { Settings } from '@coderline/alphatab/Settings'; +import { VisualTestHelper } from 'test/visualTests/VisualTestHelper'; + +/** + * Behaviour-focused coverage for the horizontal spacing subsystem. + * + * The tests in this file each target one specific invariant of the Gourlay spring-based + * spacing algorithm rather than broad rendering smoke-tests. They are intended as + * regression guards for layout changes that touch `BarLayoutingInfo`, `StaffSystem` or + * `VerticalLayoutBase._scaleToWidth`. + * + * Two axes of behaviour are covered: + * + * - **System-wide minimum-duration reference.** The Gourlay stretch formula uses the + * shortest note of the entire system (not of each bar) so that rhythmically-equivalent + * beats across bars of the same system receive spring constants derived from the same + * reference. The `shared-min-duration-*` tests exercise this end-to-end: the core fix, + * both positions of the shorter note during system assembly, multiple successive + * shortenings, per-system isolation, mode-specific variants and the resize path. + * + * - **Stretch-formula duration scaling.** The Gourlay log-scale formula produces spacing + * that grows with note duration. `stretch-formula-*` tests pin the current coefficient + * choices so an accidental change (or a deliberate formula swap) is caught explicitly + * rather than hiding inside a bulk baseline regeneration. + * + * Bar content used in the short-note tests is tuned to exactly fill 4/4 (3840 ticks) so + * that alphaTex does not auto-pad or truncate and the rendered layout reflects only the + * spacing behaviour under test. The padding pattern `:8 r :16 r :32 r :64 r` (= 900 ticks) + * pairs with "3 quarters + a 60-tick short-duration slot" (128th triplet or four 256ths) + * to reach the bar total. + * + * The short-duration slot uses rests rather than notes - a `:128 {tu 3} r r r` still + * pushes the bar-local minimum duration to 20 ticks (which is what the phase-B tests + * need) but renders as compact rest glyphs. Using notes in that slot would stack three + * noteheads at almost the same x-position under heavy 128th beaming, producing a visually + * unreadable cluster that obscures the quarters we actually want to compare across bars. + */ +describe('SystemSpacingTests', () => { + it('shared-min-duration-aligns-same-duration-notes', async () => { + // Bar 1: four quarters. No sub-30-tick spring, so the bar-local minimum falls back + // to the default reference (30 ticks). + // Bar 2: three quarters plus a 128th-triplet rest slot (20 ticks per rest), + // pushing the bar-local minimum below the default. With per-bar minimums the two + // bars would produce different spring constants for their shared quarter-note + // positions; with a shared system minimum they produce the same spring constant, + // so the quarters in bar 1 and bar 2 line up column-by-column. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 2 } + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r + `, + 'test-data/visual-tests/system-spacing/shared-min-duration-aligns-same-duration-notes.png', + settings + ); + }); + + it('shared-min-duration-reconciles-on-resize', async () => { + // Same score content as the alignment test, rendered at a narrower width so that + // the reconcile path runs under a tighter fit (distributable share smaller, force + // differs from the wider case). Guards the reconcile-on-fit path when a system is + // re-sized after layout. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 2 } + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r + `, + '', + settings, + o => { + o.runs[0].width = 900; + o.runs[0].referenceFileName = + 'test-data/visual-tests/system-spacing/shared-min-duration-reconciles-on-resize.png'; + } + ); + }); + + it('shared-min-duration-shorter-note-first', async () => { + // Bar 1 already carries the system's shortest note (128th triplet). Bars 2 and 3 + // hold only quarters, so their bar-local minimum falls back to the default. When + // each subsequent bar is added, the system's existing minimum is already lower + // than the bar's local one, so the system minimum does not move and no reconcile + // is flagged; instead, each newly-added bar has its spring constants recomputed + // immediately so it references the system minimum. This exercises the + // "eager-recompute-on-add" branch of `_trackSystemMinDuration`. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 3 } + \\ts 4 4 + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r | + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 c4 + `, + 'test-data/visual-tests/system-spacing/shared-min-duration-shorter-note-first.png', + settings + ); + }); + + it('shared-min-duration-multiple-short-arrivals', async () => { + // Successive shortenings of the system minimum: bar 2 introduces a 128th triplet + // rest (20 ticks per rest), bar 4 introduces 256th rests (15 ticks each). When + // bar 2 is added the reconcile flag is set for bar 1; when bar 4 is added the + // flag is re-asserted for bars 1-3. A single reconcile pass at fit time must + // re-derive all stale bars in one iteration, not once per shortening event. + // + // Bar 4's padding uses an extra `:128 r` (30 ticks) compared to bar 2, because + // bar 4's short slot is only 30 ticks (two 256th rests) whereas bar 2's is 60 + // ticks (three 128th-triplet rests). + // + // This test is rendered at a wider canvas (1800 px) than the rest of the suite + // because the system is forced to hold all four heavy bars in a single row. + // The natural content width at min-stretch-force (dominated by the 256th-rest + // spring, bbox ~2.16 staff spaces = ~16 px) exceeds the default 1300 px staff, + // which would push content-share below 1 and force the 256th rests below their + // minimum spacing - producing visible glyph overlap that is a rendering artefact + // of the over-packed system, not of the spacing algorithm. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 4 } + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r | + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :256 r r :8 r :16 r :32 r :64 r :128 r + `, + '', + settings, + o => { + o.runs[0].width = 1800; + o.runs[0].referenceFileName = + 'test-data/visual-tests/system-spacing/shared-min-duration-multiple-short-arrivals.png'; + } + ); + }); + + it('shared-min-duration-per-system-isolation', async () => { + // Four bars laid out as two systems of two bars each. System 1 holds pure quarters + // (local minimum falls back to the default). System 2 holds quarters plus a 128th + // triplet (minimum drops to 20 ticks). Because the shared-minimum reference lives + // on `StaffSystem` rather than being score-wide, system 1's spacing must not be + // influenced by system 2's shorter notes - each system's bars use only their own + // system's minimum. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 2 } + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r + `, + 'test-data/visual-tests/system-spacing/shared-min-duration-per-system-isolation.png', + settings + ); + }); + + it('shared-min-duration-page-automatic', async () => { + // Same two-bar score as the core alignment test, but rendered in Page layout with + // the Automatic systems mode (the default). In this mode the system distributes + // the distributable staff width across bars by their natural content width, not + // by authored `displayScale`. The system-wide minimum fix applies equally here: + // bar 1 and bar 2 share a reference so their quarter-note spring constants match, + // and each bar's natural content width reflects the shared spacing. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 2 } + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r + `, + 'test-data/visual-tests/system-spacing/shared-min-duration-page-automatic.png', + settings + ); + }); + + it('shared-min-duration-horizontal-preserves-local', async () => { + // Horizontal layout sizes each bar independently (by `bar.displayWidth` or the + // bar's intrinsic width) - there is no shared staff width to distribute across + // bars. The `StaffSystem.shareMinDurationAcrossBars` flag is set to `false` for + // this layout so each bar's spring constants stay referenced against its local + // minimum, preserving the historical per-bar rendering. This test guards the + // opt-out: under shared-minimum bar 1 would have its quarters widened to match + // bar 2's reference, which must NOT happen in Horizontal. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Horizontal; + await VisualTestHelper.runVisualTestTex( + ` + \\ts 4 4 + :4 c4 c4 c4 c4 | + :4 c4 c4 c4 :128 {tu 3} r r r :8 r :16 r :32 r :64 r + `, + 'test-data/visual-tests/system-spacing/shared-min-duration-horizontal-preserves-local.png', + settings + ); + }); + + it('stretch-formula-duration-spacing', async () => { + // A single bar with three distinct durations - half, quarter, two eighths - laid + // out in decreasing order. Under the current log-scale Gourlay formula + // `phi = 1 + 0.85 * log2(duration / minDuration)` the half-note occupies visibly + // more horizontal space than the quarter, which occupies more than each eighth. + // No short notes are involved, so this test also validates the non-phase-B path + // and pins the current formula's proportions: a coefficient change or a switch to + // linear / square-root spacing would shift this baseline. + const settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\track { defaultSystemsLayout 1 } + \\ts 4 4 + :2 c4 :4 c4 :8 c4 c4 + `, + 'test-data/visual-tests/system-spacing/stretch-formula-duration-spacing.png', + settings + ); + }); +}); diff --git a/packages/alphatab/test/visualTests/features/SystemsLayout.test.ts b/packages/alphatab/test/visualTests/features/SystemsLayout.test.ts index d4f810fe3..afdb098c4 100644 --- a/packages/alphatab/test/visualTests/features/SystemsLayout.test.ts +++ b/packages/alphatab/test/visualTests/features/SystemsLayout.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { SystemsLayoutMode } from '@coderline/alphatab/DisplaySettings'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; diff --git a/packages/alphatab/test/visualTests/issues/BrokenRenders.test.ts b/packages/alphatab/test/visualTests/issues/BrokenRenders.test.ts index e9ecf815a..26f0a017a 100644 --- a/packages/alphatab/test/visualTests/issues/BrokenRenders.test.ts +++ b/packages/alphatab/test/visualTests/issues/BrokenRenders.test.ts @@ -1,8 +1,8 @@ +import { describe, expect, it } from 'vitest'; import { TestPlatform } from 'test/TestPlatform'; import { VisualTestHelper, VisualTestOptions, VisualTestRun } from '../VisualTestHelper'; import { Settings } from '@coderline/alphatab/Settings'; import { XmlDocument } from '@coderline/alphatab/xml/XmlDocument'; -import { expect } from 'chai'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import type { RenderFinishedEventArgs } from '@coderline/alphatab/rendering/RenderFinishedEventArgs'; import { ScoreRenderer } from '@coderline/alphatab/rendering/ScoreRenderer'; @@ -83,8 +83,8 @@ describe('BrokenRendersTests', () => { const xml = new XmlDocument(); xml.parse(r.renderResult as string); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('svg'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('svg'); } } }); diff --git a/packages/alphatab/test/visualTests/issues/TranspositionTonality.test.ts b/packages/alphatab/test/visualTests/issues/TranspositionTonality.test.ts index 82d492d7e..d96647f25 100644 --- a/packages/alphatab/test/visualTests/issues/TranspositionTonality.test.ts +++ b/packages/alphatab/test/visualTests/issues/TranspositionTonality.test.ts @@ -1,3 +1,4 @@ +import { describe, it } from 'vitest'; import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader'; import { LayoutMode } from '@coderline/alphatab/LayoutMode'; import { Settings } from '@coderline/alphatab/Settings'; diff --git a/packages/alphatab/test/xml/XmlParse.test.ts b/packages/alphatab/test/xml/XmlParse.test.ts index 8b2de0b94..2ed047eab 100644 --- a/packages/alphatab/test/xml/XmlParse.test.ts +++ b/packages/alphatab/test/xml/XmlParse.test.ts @@ -1,81 +1,80 @@ +import { describe, expect, it } from 'vitest'; import { XmlDocument } from '@coderline/alphatab/xml/XmlDocument'; import { XmlNodeType } from '@coderline/alphatab/xml/XmlNode'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - describe('XmlParseTest', () => { it('parseSimple', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.childNodes.length).to.equal(0); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.childNodes.length).toBe(0); }); it('parseShorthand', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.childNodes.length).to.equal(0); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.childNodes.length).toBe(0); }); it('parseSingleAttribute', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.getAttribute('att')).to.equal('v'); - expect(xml.firstElement!.childNodes.length).to.equal(0); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.getAttribute('att')).toBe('v'); + expect(xml.firstElement!.childNodes.length).toBe(0); }); it('parseMultipleAttributes', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.getAttribute('att')).to.equal('v'); - expect(xml.firstElement!.getAttribute('att2')).to.equal('v2'); - expect(xml.firstElement!.childNodes.length).to.equal(0); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.getAttribute('att')).toBe('v'); + expect(xml.firstElement!.getAttribute('att2')).toBe('v2'); + expect(xml.firstElement!.childNodes.length).toBe(0); }); it('parseSimpleText', () => { const s: string = 'Text'; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.childNodes.length).to.equal(1); - expect(xml.firstElement!.childNodes[0].nodeType).to.equal(XmlNodeType.Text); - expect(xml.firstElement!.childNodes[0].value).to.equal('Text'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.childNodes.length).toBe(1); + expect(xml.firstElement!.childNodes[0].nodeType).toBe(XmlNodeType.Text); + expect(xml.firstElement!.childNodes[0].value).toBe('Text'); }); it('parseChild', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.childNodes.length).to.equal(1); - expect(xml.firstElement!.childNodes[0].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[0].localName).to.equal('cc'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.childNodes.length).toBe(1); + expect(xml.firstElement!.childNodes[0].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[0].localName).toBe('cc'); }); it('parseMultiChild', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); - expect(xml.firstElement!.childNodes.length).to.equal(2); - expect(xml.firstElement!.childNodes[0].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[0].localName).to.equal('cc'); - expect(xml.firstElement!.childNodes[1].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[1].localName).to.equal('cc'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); + expect(xml.firstElement!.childNodes.length).toBe(2); + expect(xml.firstElement!.childNodes[0].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[0].localName).toBe('cc'); + expect(xml.firstElement!.childNodes[1].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[1].localName).toBe('cc'); }); it('parseComments', () => { @@ -83,44 +82,44 @@ describe('XmlParseTest', () => { 'value'; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('test'); - expect(xml.firstElement!.childNodes.length).to.equal(2); - expect(xml.firstElement!.childNodes[0].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[0].localName).to.equal('cc'); - expect(xml.firstElement!.childNodes[0].getAttribute('c')).to.equal('d'); - expect(xml.firstElement!.childNodes[1].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[1].localName).to.equal('cc'); - expect(xml.firstElement!.childNodes[1].childNodes.length).to.equal(1); - expect(xml.firstElement!.childNodes[1].childNodes[0].nodeType).to.equal(XmlNodeType.Text); - expect(xml.firstElement!.childNodes[1].childNodes[0].value).to.equal('value'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('test'); + expect(xml.firstElement!.childNodes.length).toBe(2); + expect(xml.firstElement!.childNodes[0].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[0].localName).toBe('cc'); + expect(xml.firstElement!.childNodes[0].getAttribute('c')).toBe('d'); + expect(xml.firstElement!.childNodes[1].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[1].localName).toBe('cc'); + expect(xml.firstElement!.childNodes[1].childNodes.length).toBe(1); + expect(xml.firstElement!.childNodes[1].childNodes[0].nodeType).toBe(XmlNodeType.Text); + expect(xml.firstElement!.childNodes[1].childNodes[0].value).toBe('value'); }); it('parseDoctype', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('test'); - expect(xml.firstElement!.childNodes.length).to.equal(2); - expect(xml.firstElement!.childNodes[0].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[0].localName).to.equal('cc'); - expect(xml.firstElement!.childNodes[1].nodeType).to.equal(XmlNodeType.Element); - expect(xml.firstElement!.childNodes[1].localName).to.equal('cc'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('test'); + expect(xml.firstElement!.childNodes.length).toBe(2); + expect(xml.firstElement!.childNodes[0].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[0].localName).toBe('cc'); + expect(xml.firstElement!.childNodes[1].nodeType).toBe(XmlNodeType.Element); + expect(xml.firstElement!.childNodes[1].localName).toBe('cc'); }); it('parseXmlHeadTest', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; - expect(xml.firstElement!.localName).to.equal('root'); + expect(xml.firstElement).toBeTruthy(); + expect(xml.firstElement!.localName).toBe('root'); }); it('parseFull', async () => { const s = await TestPlatform.loadFileAsString('test-data/xml/GPIF.xml'); const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.firstElement).to.be.ok; + expect(xml.firstElement).toBeTruthy(); }); }); diff --git a/packages/alphatab/test/xml/XmllWrite.test.ts b/packages/alphatab/test/xml/XmllWrite.test.ts index 1a913d739..3cf6538bd 100644 --- a/packages/alphatab/test/xml/XmllWrite.test.ts +++ b/packages/alphatab/test/xml/XmllWrite.test.ts @@ -1,82 +1,81 @@ +import { describe, expect, it } from 'vitest'; import { XmlDocument } from '@coderline/alphatab/xml/XmlDocument'; import { XmlNode, XmlNodeType } from '@coderline/alphatab/xml/XmlNode'; -import { expect } from 'chai'; - describe('XmlWriteTest', () => { it('writeSimple', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(''); + expect(xml.toFormattedString()).toBe(''); }); it('writeSingleAttribute', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(''); + expect(xml.toFormattedString()).toBe(''); }); it('writeMultipleAttributes', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(''); + expect(xml.toFormattedString()).toBe(''); }); it('writeSimpleText', () => { const s: string = 'Text'; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(s); + expect(xml.toFormattedString()).toBe(s); }); it('writeSimpleTextFormatted', () => { const s: string = 'Text'; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString(' ')).to.equal(s); + expect(xml.toFormattedString(' ')).toBe(s); }); it('writeChild', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(''); - expect(xml.toFormattedString(' ')).to.equal('\n \n'); - expect(xml.toFormattedString(' ')).to.equal('\n \n'); + expect(xml.toFormattedString()).toBe(''); + expect(xml.toFormattedString(' ')).toBe('\n \n'); + expect(xml.toFormattedString(' ')).toBe('\n \n'); }); it('writeNumber', () => { const s: string = '0.5'; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal('0.5'); + expect(xml.toFormattedString()).toBe('0.5'); }); it('writeMultiChild', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(''); - expect(xml.toFormattedString(' ')).to.equal('\n \n \n'); - expect(xml.toFormattedString(' ')).to.equal('\n \n \n'); + expect(xml.toFormattedString()).toBe(''); + expect(xml.toFormattedString(' ')).toBe('\n \n \n'); + expect(xml.toFormattedString(' ')).toBe('\n \n \n'); }); it('writeXmlHeadTest', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString('', true)).to.equal(''); - expect(xml.toFormattedString(' ', true)).to.equal('\n'); + expect(xml.toFormattedString('', true)).toBe(''); + expect(xml.toFormattedString(' ', true)).toBe('\n'); }); it('writeDoctype', () => { const s: string = ''; const xml: XmlDocument = new XmlDocument(); xml.parse(s); - expect(xml.toFormattedString()).to.equal(s); - expect(xml.toFormattedString(' ')).to.equal('\n'); + expect(xml.toFormattedString()).toBe(s); + expect(xml.toFormattedString(' ')).toBe('\n'); }); it('writeEscapedAttributeValues', () => { @@ -88,7 +87,7 @@ describe('XmlWriteTest', () => { xml.firstElement!.attributes.set('amp', '&'); xml.firstElement!.attributes.set('apos', "'"); xml.firstElement!.attributes.set('quot', '"'); - expect(xml.toFormattedString()).to.equal(''); + expect(xml.toFormattedString()).toBe(''); }); it('writeComment', () => { const s: string = ''; @@ -101,6 +100,6 @@ describe('XmlWriteTest', () => { alphaTabComment.nodeType = XmlNodeType.Comment; alphaTabComment.value = 'Written by alphaTab'; xml.firstElement!.addChild(alphaTabComment); - expect(xml.toFormattedString()).to.equal(''); + expect(xml.toFormattedString()).toBe(''); }); }); diff --git a/packages/alphatab/test/zip/ZipReaderWriter.test.ts b/packages/alphatab/test/zip/ZipReaderWriter.test.ts index fff4d2f0e..5f236b65e 100644 --- a/packages/alphatab/test/zip/ZipReaderWriter.test.ts +++ b/packages/alphatab/test/zip/ZipReaderWriter.test.ts @@ -1,18 +1,18 @@ +import { describe, expect, it } from 'vitest'; import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; import { IOHelper } from '@coderline/alphatab/io/IOHelper'; import { ZipEntry } from '@coderline/alphatab/zip/ZipEntry'; import { ZipReader } from '@coderline/alphatab/zip/ZipReader'; import { ZipWriter } from '@coderline/alphatab/zip/ZipWriter'; import { TestPlatform } from 'test/TestPlatform'; -import { expect } from 'chai'; - +import { OverflowError } from '@coderline/alphatab/io/IReadable'; describe('ZipReaderWriter', () => { it('simple-read', async () => { const data = await TestPlatform.loadFile('test-data/guitarpro7/score-info.gp'); - const reader = new ZipReader(ByteBuffer.fromBuffer(data)); + const reader = new ZipReader(ByteBuffer.fromBuffer(data), 128000000); const entries = reader.read(); - expect(entries.map(e => e.fileName).join(',')).to.equal( + expect(entries.map(e => e.fileName).join(',')).toBe( 'Content/,BinaryStylesheet,LayoutConfiguration,PartConfiguration,Preferences.json,score.gpif,VERSION' ); expect( @@ -20,7 +20,7 @@ describe('ZipReaderWriter', () => { .map(e => e.data.length) .map(i => i.toString()) .join(',') - ).to.equal('0,19651,14,27,192,22998,3'); + ).toBe('0,19651,14,27,192,22998,3'); }); it('simple-roundtrip', () => { @@ -46,16 +46,81 @@ describe('ZipReaderWriter', () => { writer.end(); data.position = 0; - const reader = new ZipReader(data); + const reader = new ZipReader(data, 128000000); const entries = reader.read(); - expect(entries[0].fileName).to.equal('File01.txt'); - expect(IOHelper.toString(entries[0].data, 'utf-8')).to.equal('File01'); + expect(entries[0].fileName).toBe('File01.txt'); + expect(IOHelper.toString(entries[0].data, 'utf-8')).toBe('File01'); + + expect(entries[2].fileName).toBe('File02.txt'); + expect(IOHelper.toString(entries[2].data, 'utf-8')).toBe('File02'); + + expect(entries[3].fileName).toBe('LargeFile'); + expect(IOHelper.toString(entries[3].data, 'utf-8')).toBe(text); + }); + + describe('corrupt', () => { + async function corruptTest( + maxBuffer: number, + mainpulate: (buffer: Uint8Array) => void, + expectedOverflowLabel: string + ) { + const buffer = await TestPlatform.loadFile(`test-data/corrupt/healthy.gp`); + + mainpulate(buffer); + + const importer = new ZipReader(ByteBuffer.fromBuffer(buffer), maxBuffer); + + try { + importer.read(); + throw new Error('Expected zip read to fail with an OverflowError'); + } catch (e) { + if (e instanceof OverflowError) { + expect((e as OverflowError).message).toContain(expectedOverflowLabel); + return; + } + throw e; + } + } + + // properly announce compressed size which exceeds the range (100kb max, 300kb announced) + it('uncompressed-size', async () => + await corruptTest( + 100000, + buffer => { + const intToWrite = 300000; + buffer[22] = (intToWrite >> 0) & 0xff; + buffer[23] = (intToWrite >> 8) & 0xff; + buffer[24] = (intToWrite >> 16) & 0xff; + buffer[25] = (intToWrite >> 24) & 0xff; + }, + 'contains files exceeding' + )); - expect(entries[2].fileName).to.equal('File02.txt'); - expect(IOHelper.toString(entries[2].data, 'utf-8')).to.equal('File02'); + // properly announce filename size which exceeds the range (30kb max, 31kb announced) + it('filename', async () => + await corruptTest( + 30000, + buffer => { + const shortToWrite = 31000; + buffer[26] = (shortToWrite >> 0) & 0xff; + buffer[27] = (shortToWrite >> 8) & 0xff; + }, + 'contains file names exceeding' + )); - expect(entries[3].fileName).to.equal('LargeFile'); - expect(IOHelper.toString(entries[3].data, 'utf-8')).to.equal(text); + // unexpected large inflation (binary stylesheet is ~21kb, limit to 15kb and fake binary stylesheet to be in-limit ) + it('inflate', async () => + await corruptTest( + 15000, + buffer => { + const intToWrite = 10000; + buffer[60] = (intToWrite >> 0) & 0xff; + buffer[61] = (intToWrite >> 8) & 0xff; + buffer[62] = (intToWrite >> 16) & 0xff; + buffer[63] = (intToWrite >> 24) & 0xff; + }, + 'Zip entry "Content/BinaryStylesheet" contains data' + )); }); }); diff --git a/packages/alphatab/tsconfig.json b/packages/alphatab/tsconfig.json index 8ff166663..4c409eded 100644 --- a/packages/alphatab/tsconfig.json +++ b/packages/alphatab/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "../../tsconfig.base.json", + "extends": "../../tsconfig.base.json" } \ No newline at end of file diff --git a/packages/alphatab/vite.config.ts b/packages/alphatab/vite.config.ts index 073747e95..5a43c6738 100644 --- a/packages/alphatab/vite.config.ts +++ b/packages/alphatab/vite.config.ts @@ -1,12 +1,12 @@ import path from 'node:path'; import url from 'node:url'; -import type { RollupTypescriptOptions } from '@rollup/plugin-typescript'; +import type { Class, VariableDeclaration } from '@oxc-project/types'; import MagicString from 'magic-string'; +import { parseAst } from 'rolldown/parseAst'; import type { OutputOptions } from 'rollup'; import { defineConfig, type LibraryOptions, type Plugin } from 'vite'; import { viteStaticCopy } from 'vite-plugin-static-copy'; -import { defaultBuildUserConfig, umd, esm as defaultEsm, dtsPathsTransformer } from '../tooling/src/vite'; -import { elementStyleUsingTransformer } from '../tooling/src/typescript'; +import { addDts, defaultBuildUserConfig, esm, umd } from '../tooling/src/vite'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); @@ -30,13 +30,67 @@ const adjustScriptPathsPlugin = (min: boolean) => { } satisfies Plugin; }; +// Rolldown unconditionally lowers top-level `class X { ... }` declarations to +// `var X = class { ... };` (documented as TDZ-hoisting behaviour, independent +// of `output.topLevelVar`). Downstream tooling that pattern-matches the +// published bundle on `class X` substrings — like the alphatab webpack plugin +// — depends on the declaration form, so we restore it here. +const preserveClassDeclarationsPlugin = (): Plugin => ({ + name: 'preserve-class-declarations', + renderChunk(code) { + const program = parseAst(code, { lang: 'js', sourceType: 'module' }); + const ms = new MagicString(code); + let changed = false; + + for (const stmt of program.body) { + if (stmt.type !== 'VariableDeclaration') { + continue; + } + const varStmt = stmt as VariableDeclaration; + if (varStmt.declarations.length !== 1) { + continue; + } + const decl = varStmt.declarations[0]; + if (decl.id.type !== 'Identifier' || !decl.init || decl.init.type !== 'ClassExpression') { + continue; + } + const classExpr = decl.init as Class; + // skip when the inner name differs from the outer binding — that + // would change semantics if the body self-references the inner name. + if (classExpr.id && classExpr.id.name !== decl.id.name) { + continue; + } + + // drop `var = ` prefix, leaving the class expression + ms.remove(varStmt.start, classExpr.start); + // promote to class declaration: insert the binding name after `class` + // if it isn't already present as an inner name. + if (!classExpr.id) { + ms.appendLeft(classExpr.start + 'class'.length, ` ${decl.id.name}`); + } + // drop the trailing semicolon of the var statement + if (code[varStmt.end - 1] === ';') { + ms.remove(varStmt.end - 1, varStmt.end); + } + changed = true; + } + + if (!changed) { + return null; + } + return { code: ms.toString(), map: ms.generateMap({ hires: 'boundary' }) }; + } +}); + export default defineConfig(({ mode }) => { - const config = defaultBuildUserConfig(); + const config = defaultBuildUserConfig(__dirname); config.plugins!.push( viteStaticCopy({ + // `stripBase` flattens so files land directly under `font/` and + // `soundfont/` instead of preserving the `font/bravura/` prefix. targets: [ - { src: 'font/bravura/*', dest: 'font/' }, - { src: 'font/sonivox/*', dest: 'soundfont/' } + { src: 'font/bravura/*', dest: 'font/', rename: { stripBase: true } }, + { src: 'font/sonivox/*', dest: 'soundfont/', rename: { stripBase: true } } ] }) ); @@ -44,45 +98,37 @@ export default defineConfig(({ mode }) => { const lib = config.build!.lib! as LibraryOptions; lib.name = 'alphaTab'; - const typeScriptOptions = (): Partial => { - return { - transformers: { - before: [elementStyleUsingTransformer()], - afterDeclarations: [dtsPathsTransformer()] - } - }; - }; - - const esm = (name: string, entry: string) => { - defaultEsm( - config, - __dirname, - name, - entry, - typeScriptOptions(), - chunk => !chunk.facadeModuleId!.endsWith('alphaTab.core.ts') - ); - - (config.build!.rollupOptions!.external as string[]).push('@coderline/alphatab/alphaTab.core'); - }; - switch (mode) { case 'umd': - umd(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts', typeScriptOptions(), true); + umd(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts', true); break; //case 'esm': - default: - esm('alphaTab', 'src/alphaTab.main.ts'); - const entry = lib.entry! as Record; + default: { + esm(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts'); + + const entry = lib.entry as Record; entry['alphaTab.core'] = path.resolve(__dirname, 'src/alphaTab.core.ts'); entry['alphaTab.worker'] = path.resolve(__dirname, 'src/alphaTab.worker.ts'); entry['alphaTab.worklet'] = path.resolve(__dirname, 'src/alphaTab.worklet.ts'); + (config.build!.rollupOptions!.external as string[]).push('@coderline/alphatab/alphaTab.core'); + for (const output of config.build!.rollupOptions!.output as OutputOptions[]) { const isMin = (output.entryFileNames as string).includes('.min'); - (output.plugins as Plugin[]).push(adjustScriptPathsPlugin(isMin)); + (output.plugins as Plugin[]).push( + adjustScriptPathsPlugin(isMin), + preserveClassDeclarationsPlugin() + ); } + + // alphaTab.core is an internal runtime-split JS chunk; its types + // are already re-exported through alphaTab.main, so no separate + // bundled `alphaTab.core.d.ts` is published. + addDts(config, __dirname, { + shouldEmitForChunk: chunk => !chunk.facadeModuleId!.endsWith('alphaTab.core.ts') + }); break; + } } return config; diff --git a/packages/alphatab/vitest.config.ts b/packages/alphatab/vitest.config.ts new file mode 100644 index 000000000..e60cfba6d --- /dev/null +++ b/packages/alphatab/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineVitestConfig } from '../tooling/src/vitest'; + +export default defineVitestConfig({ + setupFiles: ['./test/global-hooks.ts'], + truncateThreshold: 0 +}); diff --git a/packages/alphatex/package.json b/packages/alphatex/package.json index 8bbcc72c0..a9414a5e7 100644 --- a/packages/alphatex/package.json +++ b/packages/alphatex/package.json @@ -1,17 +1,20 @@ { "name": "@coderline/alphatab-alphatex", - "version": "1.8.2", + "version": "1.8.3", "private": true, "scripts": { "lint": "biome lint", "typecheck": "tsc --noEmit", + "test": "vitest run", + "test-web": "npm run test", "generate": "tsx scripts/generate.ts" }, "devDependencies": { "rimraf": "^6.1.3", "tslib": "^2.8.1", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^6.0.3", + "vitest": "^4.1.6" }, "type": "module" } diff --git a/packages/alphatex/vitest.config.ts b/packages/alphatex/vitest.config.ts new file mode 100644 index 000000000..cb8d7de0d --- /dev/null +++ b/packages/alphatex/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineVitestConfig } from '../tooling/src/vitest'; + +export default defineVitestConfig(); diff --git a/packages/csharp/package.json b/packages/csharp/package.json index 5932dcfc8..fb1a303a0 100644 --- a/packages/csharp/package.json +++ b/packages/csharp/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-csharp", - "version": "1.8.2", + "version": "1.8.3", "description": "The C# target of alphaTab.", "private": true, "type": "module", @@ -9,10 +9,12 @@ "transpile": "tsx scripts/transpile.ts", "typecheck": "tsc --noEmit", "build": "npm run transpile && cd src && dotnet build -c Release", - "test": "cd src && dotnet test -c Release" + "test": "cd src && dotnet test -c Release", + "test-ci": "cd src && dotnet test -c Release --logger GitHubActions --logger \"console;verbosity=normal\"" }, "devDependencies": { "@coderline/alphatab-transpiler": "*", - "rimraf": "^6.1.3" + "rimraf": "^6.1.3", + "typescript": "^6.0.3" } } diff --git a/packages/csharp/src/AlphaTab.Test/AlphaTab.Test.csproj b/packages/csharp/src/AlphaTab.Test/AlphaTab.Test.csproj index 5b98406ad..2cdd745a4 100644 --- a/packages/csharp/src/AlphaTab.Test/AlphaTab.Test.csproj +++ b/packages/csharp/src/AlphaTab.Test/AlphaTab.Test.csproj @@ -10,6 +10,7 @@ + diff --git a/packages/csharp/src/AlphaTab.Test/Test/Globals.cs b/packages/csharp/src/AlphaTab.Test/Test/Globals.cs index 2c77499f1..1634e4879 100644 --- a/packages/csharp/src/AlphaTab.Test/Test/Globals.cs +++ b/packages/csharp/src/AlphaTab.Test/Test/Globals.cs @@ -44,7 +44,7 @@ public static string UseSnapshotValue(string baseName, string hint) { if (!string.IsNullOrEmpty(hint)) { - baseName += $": {hint}"; + baseName += $" > {hint}"; } var value = SnapshotAssertionCounters.GetValueOrDefault(baseName) + 1; @@ -56,7 +56,6 @@ public static string UseSnapshotValue(string baseName, string hint) internal class NotExpector { private readonly T? _actual; - public NotExpector Be => this; private readonly string? _message; public NotExpector(T? actual, string? message = null) @@ -65,6 +64,21 @@ public NotExpector(T? actual, string? message = null) _message = message; } + public void Equal(object? expected, string? message = null) + { + if (expected is int i && _actual is double) + { + expected = (double)i; + } + + if (expected is double d && _actual is int) + { + expected = (int)d; + } + + Assert.AreNotEqual(expected, _actual, message ?? _message); + } + public void Ok() { if (_actual is string s) @@ -77,10 +91,99 @@ public void Ok() } } - public void Undefined() + public void ToBe(object? expected) + { + Equal(expected); + } + + public void ToEqual(object? expected, string? message = null) + { + Equal(expected, message); + } + + public void ToBeTruthy() + { + Ok(); + } + + public void ToBeFalsy() + { + if (_actual is null) + { + Assert.Fail(_message ?? "Expected non-falsy value"); + return; + } + if (_actual is bool b) + { + Assert.IsTrue(b, _message); + return; + } + if (_actual is string s) + { + Assert.IsFalse(string.IsNullOrEmpty(s), _message); + return; + } + if (_actual is IConvertible c) + { + Assert.AreNotEqual(0.0, c.ToDouble(System.Globalization.CultureInfo.InvariantCulture), _message); + return; + } + } + + public void ToContain(object element) + { + if (_actual is ICollection collection) + { + CollectionAssert.DoesNotContain(collection, element, _message); + } + else + { + Assert.Fail("Contain can only be used with collection operands"); + } + } + + public void ToHaveLength(int length) + { + if (_actual is ICollection collection) + { + Assert.AreNotEqual(length, collection.Count, _message); + } + else + { + Assert.Fail("Length can only be used with collection operands"); + } + } + + public void ToBeGreaterThan(double expected) + { + if (_actual is IComparable d) + { + Assert.IsFalse(d.CompareTo(expected) > 0, _message); + } + } + + public void ToBeLessThan(double expected) + { + if (_actual is IComparable d) + { + Assert.IsFalse(d.CompareTo(expected) < 0, _message); + } + } + + public void ToBeNull() { Assert.IsNotNull(_actual, _message); } + + public void ToBeUndefined() + { + Assert.IsNotNull(_actual, _message); + } + + public void ToBeInstanceOf(Type expected) + { + Assert.IsNotInstanceOfType(_actual, expected, _message); + } } internal class Expector @@ -94,16 +197,11 @@ public Expector(T? actual, string? message = null) _message = message; } - public Expector To => this; - public NotExpector Not() { return new NotExpector(_actual, _message); } - public Expector Be => this; - public Expector Have => this; - public void Equal(object? expected, string? message = null) { if (expected is int i && _actual is double) @@ -164,19 +262,94 @@ public void ToBe(object expected) expected = (double)i; } + if (expected is double d && _actual is int) + { + expected = (int)d; + } + Assert.AreEqual(expected, _actual, _message); } - public void Ok() + public void ToBeTruthy() { - Assert.AreNotEqual(default!, _actual, _message); + Ok(); + } + + public void ToBeFalsy() + { + if (_actual is null) + { + return; + } + if (_actual is bool b) + { + Assert.IsFalse(b, _message); + return; + } + if (_actual is string s) + { + Assert.IsTrue(string.IsNullOrEmpty(s), _message); + return; + } + if (_actual is IConvertible c) + { + Assert.AreEqual(0.0, c.ToDouble(System.Globalization.CultureInfo.InvariantCulture), _message); + return; + } + Assert.Fail(_message ?? "Expected a falsy value"); + } + + public void ToBeCloseTo(double expected, int decimals = 2) + { + var delta = System.Math.Pow(10, -decimals) / 2; + CloseTo(expected, delta); + } + + public void ToContain(object element) + { + Contain(element); + } + + public void ToHaveLength(int length) + { + Length(length); + } + + public void ToBeGreaterThan(double expected, string? message = null) + { + GreaterThan(expected, message); } - public void Undefined() + public void ToBeLessThan(double expected) + { + LessThan(expected); + } + + public void ToBeInstanceOf(Type expected) + { + Assert.IsInstanceOfType(_actual, expected, _message); + } + + public void ToBeNull() + { + Assert.IsNull(_actual, _message); + } + + public void ToBeUndefined() { Assert.IsNull(_actual, _message); } + public void ToThrow(Type expected) + { + Throw(expected); + } + + public void Ok() + { + Assert.AreNotEqual(default!, _actual, _message); + } + public void Length(int length) { if (_actual is ICollection collection) @@ -195,35 +368,16 @@ public void Contain(object element) { CollectionAssert.Contains(collection, element, _message); } - else + else if(_actual is string s) { - Assert.Fail("Contain can only be used with collection operands"); - } - } - - public void True() - { - if (_actual is bool b) - { - Assert.IsTrue(b, _message); + Assert.Contains((string)element, s, _message); } else { - Assert.Fail("ToBeTrue can only be used on bools:"); + Assert.Fail("Contain can only be used with collection operands"); } } - public void False() - { - if (_actual is bool b) - { - Assert.IsFalse(b, _message); - } - else - { - Assert.Fail("ToBeFalse can only be used on bools:"); - } - } public void Throw(Type expected) @@ -280,7 +434,7 @@ public void ToMatchSnapshot(string hint = "") .DisplayName; parts.Add(testName ?? ""); - var snapshotName = TestGlobals.UseSnapshotValue(string.Join(" ", parts), hint); + var snapshotName = TestGlobals.UseSnapshotValue(string.Join(" > ", parts), hint); var error = snapshotFile.Match(snapshotName, _actual); if (!string.IsNullOrEmpty(error)) diff --git a/packages/csharp/src/AlphaTab/Core/EcmaScript/Object.cs b/packages/csharp/src/AlphaTab/Core/EcmaScript/Object.cs index afa5dabc4..0e1cb339d 100644 --- a/packages/csharp/src/AlphaTab/Core/EcmaScript/Object.cs +++ b/packages/csharp/src/AlphaTab/Core/EcmaScript/Object.cs @@ -62,11 +62,18 @@ public static IList> Entries(object v) private static Func CompilePropertyAccessor(PropertyInfo propertyInfo) { - var param = Expression.Parameter(typeof(object)); - var castParam = Expression.Convert(param, propertyInfo.DeclaringType!); - var accessor = Expression.Property(castParam, propertyInfo); - var castReturn = Expression.Convert(accessor, typeof(object)); - return Expression.Lambda>(castReturn, param) - .Compile(); + try + { + var param = Expression.Parameter(typeof(object)); + var castParam = Expression.Convert(param, propertyInfo.DeclaringType!); + var accessor = Expression.Property(castParam, propertyInfo); + var castReturn = Expression.Convert(accessor, typeof(object)); + return Expression.Lambda>(castReturn, param) + .Compile(); + } + catch(Exception e) + { + throw new InvalidOperationException($"Failed to compile simple property accessor for property {propertyInfo.DeclaringType!.FullName}.{propertyInfo.Name}", e); + } } } diff --git a/packages/csharp/src/AlphaTab/Core/EcmaScript/TextDecoder.cs b/packages/csharp/src/AlphaTab/Core/EcmaScript/TextDecoder.cs index 6f1ca5a56..c84b8fd37 100644 --- a/packages/csharp/src/AlphaTab/Core/EcmaScript/TextDecoder.cs +++ b/packages/csharp/src/AlphaTab/Core/EcmaScript/TextDecoder.cs @@ -15,4 +15,9 @@ public string Decode(ArrayBuffer data) { return _encoding.GetString(data.Raw, 0, (int)data.ByteLength); } + + public string Decode(Uint8Array data) + { + return _encoding.GetString(data.Buffer.Raw, (int)data.ByteOffset, (int)data.Length); + } } diff --git a/packages/csharp/src/AlphaTab/Core/TypeHelper.cs b/packages/csharp/src/AlphaTab/Core/TypeHelper.cs index 95dec6afe..0ee832c13 100644 --- a/packages/csharp/src/AlphaTab/Core/TypeHelper.cs +++ b/packages/csharp/src/AlphaTab/Core/TypeHelper.cs @@ -70,6 +70,20 @@ public static T Find(this IList list, Func predicate) return list.FirstOrDefault(predicate); } + public static double FindIndex(this IList list, Func predicate) + { + var index = 0; + foreach (var item in list) + { + if (predicate(item)) + { + return index; + } + index++; + } + return -1; + } + public static bool Includes(this IList list, T item) { return list.Contains(item); diff --git a/packages/csharp/src/Directory.Build.props b/packages/csharp/src/Directory.Build.props index a7bf82dd0..5441f7065 100644 --- a/packages/csharp/src/Directory.Build.props +++ b/packages/csharp/src/Directory.Build.props @@ -2,8 +2,8 @@ portable true - 1.8.2 - 1.8.2.0 + 1.8.3 + 1.8.3.0 $(AssemblyVersion) Danielku15 CoderLine diff --git a/packages/csharp/vite.config.ts b/packages/csharp/vite.config.ts index 9913c8eec..e777b62cf 100644 --- a/packages/csharp/vite.config.ts +++ b/packages/csharp/vite.config.ts @@ -1,21 +1,15 @@ import { defaultClientMainFields, defineConfig } from 'vite'; -import { defaultBuildUserConfig, dtsPathsTransformer, esm } from '../tooling/src/vite'; +import { addDts, defaultBuildUserConfig, esm } from '../tooling/src/vite'; export default defineConfig(() => { - const config = defaultBuildUserConfig(); + const config = defaultBuildUserConfig(import.meta.dirname); config.build!.sourcemap = true; config.resolve ??= {}; config.resolve.mainFields = defaultClientMainFields.filter(f => f !== 'browser'); - esm(config, import.meta.dirname, 'server', 'src/index.ts', { - module: 'preserve', - transformers: { - afterDeclarations: [ - dtsPathsTransformer() - ] - } - }); + esm(config, import.meta.dirname, 'server', 'src/index.ts'); (config.build!.rollupOptions!.external as (RegExp | string)[]).push('@coderline/alphatab'); + addDts(config, import.meta.dirname); return config; }); diff --git a/packages/kotlin/package.json b/packages/kotlin/package.json index 1d2926a89..d4bc45536 100644 --- a/packages/kotlin/package.json +++ b/packages/kotlin/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-kotlin", - "version": "1.8.2", + "version": "1.8.3", "description": "The Kotlin target of alphaTab.", "private": true, "type": "module", diff --git a/packages/kotlin/src/android/build.gradle.kts b/packages/kotlin/src/android/build.gradle.kts index 586b77de8..bc1804845 100644 --- a/packages/kotlin/src/android/build.gradle.kts +++ b/packages/kotlin/src/android/build.gradle.kts @@ -39,7 +39,7 @@ var libAuthorId = "danielku15" var libAuthorName = "Daniel Kuschny" var libOrgUrl = "https://github.com/coderline" var libCompany = "CoderLine" -var libVersion = "1.8.2-SNAPSHOT" +var libVersion = "1.8.3-SNAPSHOT" var libProjectUrl = "https://github.com/CoderLine/alphaTab" var libGitUrlHttp = "https://github.com/CoderLine/alphaTab.git" var libGitUrlGit = "scm:git:git://github.com/CoderLine/alphaTab.git" diff --git a/packages/kotlin/src/android/src/main/java/alphaTab/collections/List.kt b/packages/kotlin/src/android/src/main/java/alphaTab/collections/List.kt index 951ada471..0c0fdff6f 100644 --- a/packages/kotlin/src/android/src/main/java/alphaTab/collections/List.kt +++ b/packages/kotlin/src/android/src/main/java/alphaTab/collections/List.kt @@ -87,6 +87,10 @@ public class List : Iterable { }) } + internal fun findIndex(predicate: (T) -> Boolean): Double { + return _data.indexOfFirst(predicate).toDouble() + } + public fun some(predicate: (T) -> Boolean): Boolean { return _data.any(predicate) } @@ -165,24 +169,42 @@ public class List : Iterable { return _data.removeAt(0) } - public fun splice(start: Double, deleteCount: Double, vararg newElements: T) { + public fun splice(start: Double, deleteCount: Double, vararg newElements: T): List { var actualStart = start.toInt() if (actualStart < 0) { actualStart += _data.size } + val remove = if (deleteCount > 0) List( + ArrayListWithRemoveRange( + _data.subList( + start.toInt(), + _data.size + ) + ) + ) else List() _data.removeRange(start.toInt(), (start + deleteCount).toInt()) _data.addAll(start.toInt(), newElements.toList()) + return remove } - public fun splice(start: Double, deleteCount: Double, newElements: Iterable) { + public fun splice(start: Double, deleteCount: Double, newElements: Iterable): List { var actualStart = start.toInt() if (actualStart < 0) { actualStart += _data.size } + val remove = if (deleteCount > 0) List( + ArrayListWithRemoveRange( + _data.subList( + start.toInt(), + _data.size + ) + ) + ) else List() _data.removeRange(start.toInt(), (start + deleteCount).toInt()) _data.addAll(start.toInt(), newElements.toList()) + return remove } public fun join(separator: String): String { diff --git a/packages/kotlin/src/android/src/main/java/alphaTab/core/Globals.kt b/packages/kotlin/src/android/src/main/java/alphaTab/core/Globals.kt index 798dee2ec..679418d08 100644 --- a/packages/kotlin/src/android/src/main/java/alphaTab/core/Globals.kt +++ b/packages/kotlin/src/android/src/main/java/alphaTab/core/Globals.kt @@ -40,12 +40,6 @@ internal inline fun UByteArray.decodeToDoubleArray(): DoubleArray { return da } -@ExperimentalUnsignedTypes -internal inline fun UByteArray.decodeToString(encoding: String): String { - return String(this.toByteArray(), 0, this.size, Charset.forName(encoding)) -} - - internal inline fun > List.sort() { this.sort { a, b -> a.compareTo(b).toDouble() diff --git a/packages/kotlin/src/android/src/main/java/alphaTab/core/ecmaScript/TextDecoder.kt b/packages/kotlin/src/android/src/main/java/alphaTab/core/ecmaScript/TextDecoder.kt index 61b58483d..59a95ecb7 100644 --- a/packages/kotlin/src/android/src/main/java/alphaTab/core/ecmaScript/TextDecoder.kt +++ b/packages/kotlin/src/android/src/main/java/alphaTab/core/ecmaScript/TextDecoder.kt @@ -1,13 +1,18 @@ package alphaTab.core.ecmaScript -import alphaTab.core.decodeToString import alphaTab.core.ecmaScript.ArrayBuffer +import java.nio.charset.Charset internal class TextDecoder(encoding:String) { private val _encoding:String = encoding @ExperimentalUnsignedTypes public fun decode(buffer: ArrayBuffer): String { - return buffer.decodeToString(_encoding) + return String(buffer.toByteArray(), 0, buffer.size, Charset.forName(_encoding)) + } + + @ExperimentalUnsignedTypes + public fun decode(buffer: Uint8Array): String { + return String(buffer.buffer.toByteArray(), buffer.byteOffset.toInt(), buffer.length.toInt(), Charset.forName(_encoding)) } } diff --git a/packages/kotlin/src/android/src/test/java/alphaTab/core/TestGlobals.kt b/packages/kotlin/src/android/src/test/java/alphaTab/core/TestGlobals.kt index c8827432e..17972d74e 100644 --- a/packages/kotlin/src/android/src/test/java/alphaTab/core/TestGlobals.kt +++ b/packages/kotlin/src/android/src/test/java/alphaTab/core/TestGlobals.kt @@ -28,6 +28,31 @@ class NotExpector(private val actual: T, private val message: String? = null) val be get() = this + fun equal(expected: Any?, message: String? = null) { + var actualToCheck = actual + var expectedTyped: Any? = expected + + if (expected is Int && actualToCheck is Double) { + expectedTyped = expected.toDouble(); + } + + if (expected is Double && actualToCheck is Int) { + expectedTyped = expected.toInt(); + } + + if (expected is Double && expected == 0.0 && + actualToCheck is Double + ) { + val d = actualToCheck as Double; + if (d == -0.0) { + @Suppress("UNCHECKED_CAST") + actualToCheck = 0.0 as T + } + } + + Assert.assertNotEquals(this.message ?: message, expectedTyped, actualToCheck as Any?) + } + fun ok() { when (actual) { is Int -> { @@ -48,23 +73,88 @@ class NotExpector(private val actual: T, private val message: String? = null) } } + fun toBe(expected: Any?) { + equal(expected) + } + + fun toEqual(expected: Any?, message: String? = null) { + equal(expected, message) + } + + fun toBeTruthy() { + ok() + } + + fun toBeFalsy() { + when (actual) { + null -> Assert.fail(message ?: "Expected non-falsy value") + is Boolean -> Assert.assertTrue(message, actual) + is String -> Assert.assertFalse(message, actual.isEmpty()) + is Number -> Assert.assertNotEquals(message, 0.0, actual.toDouble(), 0.0) + } + } + + fun toContain(value: Any) { + if (actual is Iterable<*>) { + Assert.assertFalse( + message ?: "Expected collection ${actual.joinToString(",")} to not contain $value", + actual.contains(value) + ) + } else { + Assert.fail("toContain can only be used with Iterable operands"); + } + } + + fun toHaveLength(expected: Double) { + when (actual) { + is alphaTab.collections.List<*> -> Assert.assertNotEquals(message, expected.toInt(), actual.length.toInt()) + is alphaTab.collections.DoubleList -> Assert.assertNotEquals(message, expected.toInt(), actual.length.toInt()) + is alphaTab.collections.BooleanList -> Assert.assertNotEquals(message, expected.toInt(), actual.length.toInt()) + else -> Assert.fail("toHaveLength can only be used with collection operands") + } + } + + fun toBeGreaterThan(expected: Double) { + if (actual is Number) { + Assert.assertFalse( + message ?: "Expected $actual to not be greater than $expected", + actual.toDouble() > expected + ) + } else { + Assert.fail("toBeGreaterThan can only be used with numeric operands") + } + } + + fun toBeLessThan(expected: Double) { + if (actual is Number) { + Assert.assertFalse( + message ?: "Expected $actual to not be less than $expected", + actual.toDouble() < expected + ) + } else { + Assert.fail("toBeLessThan can only be used with numeric operands") + } + } + + fun toBeNull() { + Assert.assertNotNull(message, actual) + } - fun undefined() { + fun toBeUndefined() { Assert.assertNotNull(message, actual) } + fun toBeInstanceOf(expected: kotlin.reflect.KClass<*>) { + Assert.assertFalse( + message ?: "Expected ${actual?.let { it::class.qualifiedName }} to not be instance of ${expected.qualifiedName}", + expected.isInstance(actual) + ) + } } class Expector(private val actual: T, private val message: String? = null) { - val to - get() = this - - fun not() = NotExpector(actual, message) - - val be - get() = this - val have - get() = this + val not + get() = NotExpector(actual, message) fun equal(expected: Any?, message: String? = null) { var actualToCheck = actual @@ -141,6 +231,11 @@ class Expector(private val actual: T, private val message: String? = null) { message ?: "Expected collection ${actual.joinToString(",")} to contain $value", actual.contains(value) ) + } else if(actual is String) { + Assert.assertTrue( + message ?: "Expected string $actual to contain $value", + actual.contains(value as String) + ) } else { Assert.fail("contain can only be used with Iterable operands"); } @@ -163,26 +258,67 @@ class Expector(private val actual: T, private val message: String? = null) { } } - fun undefined() { - Assert.assertNull(message, actual) + fun toBe(expected: Any?) { + equal(expected) } - fun `true`() { - if (actual is Boolean) { - Assert.assertTrue(message, actual); - } else { - Assert.fail("toBeTrue can only be used on booleans:"); - } + fun toEqual(expected: Any?, message: String? = null) { + equal(expected, message) } - fun `false`() { - if (actual is Boolean) { - Assert.assertFalse(message, actual); - } else { - Assert.fail("toBeFalse can only be used on booleans:"); + fun toBeTruthy() { + ok() + } + + fun toBeFalsy() { + when (actual) { + null -> return + is Boolean -> Assert.assertFalse(message, actual) + is String -> Assert.assertTrue(message, actual.isEmpty()) + is Number -> Assert.assertEquals(message, 0.0, actual.toDouble(), 0.0) + else -> Assert.fail(message ?: "Expected a falsy value") } } + fun toBeCloseTo(expected: Double, decimals: Double = 2.0) { + val delta = Math.pow(10.0, -decimals) / 2 + closeTo(expected, delta) + } + + fun toContain(value: Any) { + contain(value) + } + + fun toHaveLength(expected: Double) { + length(expected) + } + + fun toBeGreaterThan(expected: Double, message: String? = null) { + greaterThan(expected, message) + } + + fun toBeLessThan(expected: Double) { + lessThan(expected) + } + + fun toBeInstanceOf(expected: KClass<*>) { + Assert.assertTrue( + message ?: "Expected ${actual?.let { it::class.qualifiedName }} to be instance of ${expected.qualifiedName}", + expected.isInstance(actual) + ) + } + + fun toBeNull() { + Assert.assertNull(message, actual) + } + + fun toBeUndefined() { + Assert.assertNull(message, actual) + } + + fun toThrow(expected: KClass) { + `throw`(expected) + } fun `throw`(expected: KClass) { val actual = actual @@ -227,7 +363,7 @@ class Expector(private val actual: T, private val message: String? = null) { val testName = testMethodInfo.getAnnotation(TestName::class.java)!!.name parts.push(testName) - val snapshotName = TestGlobals.useSnapshotValue(parts.joinToString(" "), hint); + val snapshotName = TestGlobals.useSnapshotValue(parts.joinToString(" > "), hint); val error = snapshotFile.match(snapshotName, actual) if (!error.isNullOrEmpty()) { @@ -255,7 +391,7 @@ class TestGlobals { fun useSnapshotValue(baseName: String, hint: String): String { var fullName = baseName if (hint.isNotEmpty()) { - fullName += ": $hint"; + fullName += " > $hint"; } val value = diff --git a/packages/kotlin/vite.config.ts b/packages/kotlin/vite.config.ts index 9913c8eec..e777b62cf 100644 --- a/packages/kotlin/vite.config.ts +++ b/packages/kotlin/vite.config.ts @@ -1,21 +1,15 @@ import { defaultClientMainFields, defineConfig } from 'vite'; -import { defaultBuildUserConfig, dtsPathsTransformer, esm } from '../tooling/src/vite'; +import { addDts, defaultBuildUserConfig, esm } from '../tooling/src/vite'; export default defineConfig(() => { - const config = defaultBuildUserConfig(); + const config = defaultBuildUserConfig(import.meta.dirname); config.build!.sourcemap = true; config.resolve ??= {}; config.resolve.mainFields = defaultClientMainFields.filter(f => f !== 'browser'); - esm(config, import.meta.dirname, 'server', 'src/index.ts', { - module: 'preserve', - transformers: { - afterDeclarations: [ - dtsPathsTransformer() - ] - } - }); + esm(config, import.meta.dirname, 'server', 'src/index.ts'); (config.build!.rollupOptions!.external as (RegExp | string)[]).push('@coderline/alphatab'); + addDts(config, import.meta.dirname); return config; }); diff --git a/packages/lsp/package.json b/packages/lsp/package.json index 083af994b..0d3ace5f2 100644 --- a/packages/lsp/package.json +++ b/packages/lsp/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-language-server", - "version": "1.8.2", + "version": "1.8.3", "description": "A language server for alphaTab providing coding assistance for alphaTex.", "keywords": [ "guitar", @@ -31,26 +31,23 @@ "typecheck": "tsc --noEmit", "build": "vite build", "dev": "vite build --watch", - "test": "mocha" + "test": "vitest run", + "test-web": "npm run test" }, "dependencies": { - "@coderline/alphatab": "^1.8.2", + "@coderline/alphatab": "^1.8.3", "vscode-languageserver": "^9.0.1", "vscode-languageserver-textdocument": "^1.0.12" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@microsoft/api-extractor": "^7.57.7", - "@types/chai": "^5.2.2", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "mocha": "^11.7.4", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vitest": "^4.1.7" }, "bin": "./dist/server.mjs", "main": "./dist/server.mjs", diff --git a/packages/lsp/test/placeholder.test.ts b/packages/lsp/test/placeholder.test.ts index c716174a1..4be560bd7 100644 --- a/packages/lsp/test/placeholder.test.ts +++ b/packages/lsp/test/placeholder.test.ts @@ -1,3 +1,5 @@ -describe('placeholder', ()=>{ - it('tests') -}); \ No newline at end of file +import { describe, it } from 'vitest'; + +describe('placeholder', () => { + it.todo('tests'); +}); diff --git a/packages/lsp/vite.config.ts b/packages/lsp/vite.config.ts index 5ae3d87ff..1b88feddf 100644 --- a/packages/lsp/vite.config.ts +++ b/packages/lsp/vite.config.ts @@ -1,18 +1,14 @@ import { defaultClientMainFields, defineConfig } from 'vite'; -import { defaultBuildUserConfig, dtsPathsTransformer, esm } from '../tooling/src/vite'; +import { addDts, defaultBuildUserConfig, esm } from '../tooling/src/vite'; export default defineConfig(() => { - const config = defaultBuildUserConfig(); + const config = defaultBuildUserConfig(import.meta.dirname); config.build!.sourcemap = true; config.resolve ??= {}; config.resolve.mainFields = defaultClientMainFields.filter(f => f !== 'browser'); - esm(config, import.meta.dirname, 'server', 'src/index.ts', { - module: 'preserve', - transformers: { - afterDeclarations: [dtsPathsTransformer()] - } - }); + esm(config, import.meta.dirname, 'server', 'src/index.ts'); (config.build!.rollupOptions!.external as (RegExp | string)[]).push('@coderline/alphatab'); + addDts(config, import.meta.dirname); return config; }); diff --git a/packages/lsp/vitest.config.ts b/packages/lsp/vitest.config.ts new file mode 100644 index 000000000..cb8d7de0d --- /dev/null +++ b/packages/lsp/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineVitestConfig } from '../tooling/src/vitest'; + +export default defineVitestConfig(); diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 93ebd0704..68d4b1cc3 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-monaco", - "version": "1.8.2", + "version": "1.8.3", "description": "A Monaco editor integration for alphaTab providing coding assistance for alphaTex.", "keywords": [ "guitar", @@ -28,29 +28,26 @@ "typecheck": "tsc --noEmit", "build": "vite build", "dev": "vite build --watch", - "test": "mocha" + "test": "vitest run", + "test-web": "npm run test" }, "dependencies": { - "@coderline/alphatab": "^1.8.2", - "@coderline/alphatab-language-server": "^1.8.2", + "@coderline/alphatab": "^1.8.3", + "@coderline/alphatab-language-server": "^1.8.3", "monaco-editor": "^0.55.1", "vscode-languageserver-types": "^3.17.5", "vscode-oniguruma": "^2.0.1", "vscode-textmate": "^9.3.2" }, "devDependencies": { - "@biomejs/biome": "^2.4.10", + "@biomejs/biome": "^2.4.15", "@microsoft/api-extractor": "^7.57.7", - "@types/chai": "^5.2.2", - "@types/mocha": "^10.0.10", - "@types/node": "^25.5.0", - "assert": "^2.1.0", - "chai": "^6.2.2", - "mocha": "^11.7.4", + "@types/node": "^25.9.1", "rimraf": "^6.1.3", "tslib": "^2.8.1", - "tsx": "^4.21.0", - "typescript": "^5.9.3" + "tsx": "^4.22.3", + "typescript": "^6.0.3", + "vitest": "^4.1.6" }, "bin": "./dist/alphaTab.monaco.mjs", "main": "./dist/alphaTab.monaco.mjs", diff --git a/packages/monaco/test/placeholder.test.ts b/packages/monaco/test/placeholder.test.ts index c716174a1..4be560bd7 100644 --- a/packages/monaco/test/placeholder.test.ts +++ b/packages/monaco/test/placeholder.test.ts @@ -1,3 +1,5 @@ -describe('placeholder', ()=>{ - it('tests') -}); \ No newline at end of file +import { describe, it } from 'vitest'; + +describe('placeholder', () => { + it.todo('tests'); +}); diff --git a/packages/monaco/vitest.config.ts b/packages/monaco/vitest.config.ts new file mode 100644 index 000000000..cb8d7de0d --- /dev/null +++ b/packages/monaco/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineVitestConfig } from '../tooling/src/vitest'; + +export default defineVitestConfig(); diff --git a/packages/playground/README.md b/packages/playground/README.md new file mode 100644 index 000000000..1efcad704 --- /dev/null +++ b/packages/playground/README.md @@ -0,0 +1,314 @@ +# alphaTab Playground + +A development harness for alphaTab. Each demo under `demos/` exercises a specific capability of the +library against the live source under `packages/alphatab/src/`. Editing alphaTab source hot-reloads +the playground via Vite's workspace alias. + +The playground is **not** a framework reference for end users. Framework-specific components are +planned as separate packages. The patterns shown here are deliberately framework-agnostic: a +React/Angular/Svelte/Vue user can read these classes, understand which alphaTab events to subscribe +to and when to clean up, then apply the same logic in their framework's idiomatic shape. + +## Running it + +From the monorepo root: + +```bash +npm run dev +``` + +This is a shortcut for `npm run dev --workspace=packages/playground` and opens the labs index at +[`http://localhost:5173/`](http://localhost:5173/). + +Other scripts (run from `packages/playground/`): + +- `npm run typecheck` — `tsc --noEmit` against the playground's tsconfig. +- `npm run lint` — Biome lint. + +## Architecture at a glance + +``` +demos//index.html minimal "
" + module script +demos//index.ts constructs an App and appends `app.root` + +src/apps/App.ts top-level composer per demo, owns the AlphaTabApi +src/components/.ts alphaTab-aware composer (TransportBar, Footer, Sidebar, …) +src/components/primitives/ alphaTab-unaware UI bricks (IconButton, Dropdown, Slider, …) +src/util/Dom.ts html/css tagged templates, parseHtml, injectStyles, mount +src/util/Icons.ts lucide pass-through +src/util/Paths.ts default file paths +src/styles/common.css palette, fonts, resets — global CSS +``` + +The component tree for a typical demo is `App → composers → primitives`. The App constructs the +AlphaTabApi after mounting its viewport, then mounts composers into placeholders in its template. +Composers do the same for their primitives. + +## The component contract + +Every UI piece is a class with this consistent shape: + +```ts +class Transport implements Mountable { + readonly root: HTMLElement; // detached at construction; mounted by caller + + constructor(api: alphaTab.AlphaTabApi); // engine deps as args; never `parent` + + setReady(ready: boolean): void; // parent → child push API + onSomething: (() => void) | null = null; // child → parent event API + + dispose(): void; +} +``` + +Rules: + +- **Constructor takes engine dependencies and props, never `parent`.** Build DOM detached as `.root`. + Mounting is the caller's job. +- **Public methods are the parent → child push API.** Parents call `transport.setReady(true)` + directly. No prop diffing, no virtual DOM. +- **Assignable callback fields are the child → parent event API.** A child exposes + `onPlayClick: (() => void) | null = null`; the parent assigns a function. For multi-listener + fan-out, a typed `EventEmitter` is fine. +- **`dispose()` releases everything**: api event subscriptions (the `() => void` returned by + `api.event.on(...)`), DOM listeners, intervals, ResizeObservers, child components. Removing + `this.root` takes the entire DOM subtree with it. +- **Composition over inheritance.** `Footer` composes `TransportBar`; `App` composes one of each + top-level piece. +- **Engine events drive UI.** Each component subscribes directly to the alphaTab events it needs + (`TransportBar` → `playerStateChanged`; `TrackList` → `scoreLoaded`; `TimeSlider` → + `playerPositionChanged`; `Waveform` → `scoreLoaded` + DOM events on the audio element). The api + is the source of truth — no intermediate state container. + +### No IDs, kebab-prefixed classes, component-local nesting + +- **No `id` attributes inside component markup.** IDs collide across multiple instances. Use class + names or `data-*`. +- **Prefix every class with the component's kebab name** — `at-icon-btn`, `at-transport-…`, + `at-drum-pad-…`. Names are global; prefixes prevent leaks. +- **Nested selectors stay component-local** — `.at-track .at-track-controls`, never bare + `.at-track-controls`. + +### Styles co-located via `injectStyles` + +```ts +import { html, css, parseHtml, injectStyles } from '../../util/Dom'; + +injectStyles('IconButton', css` + .at-icon-btn { display: inline-flex; align-items: center; … } + .at-icon-btn[disabled] { opacity: 0.4; cursor: default; } +`); +``` + +`injectStyles(key, sheet)` is keyed by component name and injects the ` + + +
+ + + diff --git a/packages/playground/demos/alphatex-editor/index.ts b/packages/playground/demos/alphatex-editor/index.ts new file mode 100644 index 000000000..9a39a4d7b --- /dev/null +++ b/packages/playground/demos/alphatex-editor/index.ts @@ -0,0 +1,8 @@ +import { AlphaTexEditorApp } from '../../src/apps/AlphaTexEditorApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new AlphaTexEditorApp(); +root.appendChild(app.root); diff --git a/packages/playground/demos/control/index.html b/packages/playground/demos/control/index.html new file mode 100644 index 000000000..b6e992ac6 --- /dev/null +++ b/packages/playground/demos/control/index.html @@ -0,0 +1,12 @@ + + + + + alphaTab — Control + + + +
+ + + diff --git a/packages/playground/demos/control/index.ts b/packages/playground/demos/control/index.ts new file mode 100644 index 000000000..475b3d5b1 --- /dev/null +++ b/packages/playground/demos/control/index.ts @@ -0,0 +1,8 @@ +import { ControlApp } from '../../src/apps/ControlApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new ControlApp(); +root.appendChild(app.root); diff --git a/packages/playground/demos/recorder/index.html b/packages/playground/demos/recorder/index.html new file mode 100644 index 000000000..c9d3f9c94 --- /dev/null +++ b/packages/playground/demos/recorder/index.html @@ -0,0 +1,12 @@ + + + + + alphaTab — Drum Recorder + + + +
+ + + diff --git a/packages/playground/demos/recorder/index.ts b/packages/playground/demos/recorder/index.ts new file mode 100644 index 000000000..cefba3304 --- /dev/null +++ b/packages/playground/demos/recorder/index.ts @@ -0,0 +1,8 @@ +import { RecorderApp } from '../../src/apps/RecorderApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new RecorderApp(); +root.appendChild(app.root); diff --git a/packages/playground/demos/test-results/index.html b/packages/playground/demos/test-results/index.html new file mode 100644 index 000000000..01a8d066f --- /dev/null +++ b/packages/playground/demos/test-results/index.html @@ -0,0 +1,13 @@ + + + + + alphaTab — Visual Test Results + + + + +
+ + + diff --git a/packages/playground/demos/test-results/index.ts b/packages/playground/demos/test-results/index.ts new file mode 100644 index 000000000..69752e3b4 --- /dev/null +++ b/packages/playground/demos/test-results/index.ts @@ -0,0 +1,8 @@ +import { TestResultsApp } from '../../src/apps/TestResultsApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new TestResultsApp(); +root.appendChild(app.root); diff --git a/packages/playground/demos/youtube-sync/index.html b/packages/playground/demos/youtube-sync/index.html new file mode 100644 index 000000000..d8c81f9b9 --- /dev/null +++ b/packages/playground/demos/youtube-sync/index.html @@ -0,0 +1,16 @@ + + + + + alphaTab — YouTube Sync + + + + +
+ + + diff --git a/packages/playground/demos/youtube-sync/index.ts b/packages/playground/demos/youtube-sync/index.ts new file mode 100644 index 000000000..5ce2be37e --- /dev/null +++ b/packages/playground/demos/youtube-sync/index.ts @@ -0,0 +1,8 @@ +import { YoutubeSyncApp } from '../../src/apps/YoutubeSyncApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new YoutubeSyncApp(); +root.appendChild(app.root); diff --git a/packages/playground/index.html b/packages/playground/index.html new file mode 100644 index 000000000..622361af5 --- /dev/null +++ b/packages/playground/index.html @@ -0,0 +1,13 @@ + + + + + alphaTab Playground + + + + +
+ + + diff --git a/packages/playground/index.ts b/packages/playground/index.ts new file mode 100644 index 000000000..edbc0d114 --- /dev/null +++ b/packages/playground/index.ts @@ -0,0 +1,8 @@ +import { LabsIndexApp } from './src/apps/LabsIndexApp'; + +const root = document.getElementById('app'); +if (!root) { + throw new Error('#app element not found'); +} +const app = new LabsIndexApp(); +root.appendChild(app.root); diff --git a/packages/playground/package.json b/packages/playground/package.json index 658dfb6e6..0eb14d861 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -1,6 +1,6 @@ { "name": "@coderline/alphatab-playground", - "version": "1.8.2", + "version": "1.8.3", "description": "A development playground for alphaTab to test features while developing", "private": true, "type": "module", @@ -8,25 +8,24 @@ "@coderline/alphatab": "*", "@fontsource/noto-sans": "^5.2.10", "@fontsource/noto-serif": "^5.2.9", - "@fortawesome/fontawesome-free": "^7.2.0", "@popperjs/core": "^2.11.8", "@types/serve-static": "^2.2.0", - "bootstrap": "^5.3.8", - "handlebars": "^4.7.9", + "lucide": "^1.14.0", "monaco-editor": "^0.55.1", "serve-static": "^2.2.1" }, "scripts": { "lint": "biome lint", "typecheck": "tsc --noEmit", + "test": "vitest run", + "test-web": "npm run test", "dev": "vite" }, "devDependencies": { - "@types/bootstrap": "^5.2.10", "split.js": "^1.6.5", "tslib": "^2.8.1", - "typescript": "^5.9.3", - "vite": "^7.3.2", - "vite-tsconfig-paths": "^6.1.1" + "typescript": "^6.0.3", + "vite": "^8.0.14", + "vitest": "^4.1.7" } } diff --git a/packages/playground/select-handles.css b/packages/playground/select-handles.css deleted file mode 100644 index e49aa7ef6..000000000 --- a/packages/playground/select-handles.css +++ /dev/null @@ -1,35 +0,0 @@ -.at-selection-handles { - position : absolute; - pointer-events: none; - z-index: 1001; - display: inline; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.at-selection-handle { - position : absolute; - pointer-events: auto; - cursor: ew-resize; - background: #7cb9ff; - width: 4px; - opacity: 0; - transition: opacity 150ms ease-in-out; - display: none; -} - -.at-selection-handle:hover, -.at-selection-handle.at-selection-handle-drag { - opacity: 1; -} - -.at-selection-handle.active { - display: block; -} - -.at-selection-handle-drag * { - cursor: ew-resize !important; -} - diff --git a/packages/playground/select-handles.ts b/packages/playground/select-handles.ts deleted file mode 100644 index 80d3aeb29..000000000 --- a/packages/playground/select-handles.ts +++ /dev/null @@ -1,176 +0,0 @@ -import type * as alphaTab from '@coderline/alphatab'; - -interface HandleDragState { - isDragging: 'start' | 'end' | undefined; -} - -function createSelectionHandles(element: HTMLElement): { startHandle: HTMLElement; endHandle: HTMLElement } { - const handleWrapper = document.createElement('div'); - handleWrapper.classList.add('at-selection-handles'); - element.insertBefore(handleWrapper, element.querySelector('at-surface')); - - const startHandle = document.createElement('div'); - startHandle.classList.add('at-selection-handle', 'at-selection-handle-start'); - handleWrapper.appendChild(startHandle); - - const endHandle = document.createElement('div'); - endHandle.classList.add('at-selection-handle', 'at-selection-handle-end'); - handleWrapper.appendChild(endHandle); - - return { startHandle, endHandle }; -} - -function setupHandleDrag( - element: HTMLElement, - handle: HTMLElement, - dragState: HandleDragState, - type: HandleDragState['isDragging'], - onMove: (e: MouseEvent) => void, - onDragEnd: (e: MouseEvent) => void -) { - handle.addEventListener( - 'mousedown', - e => { - e.preventDefault(); - element.classList.add('at-selection-handle-drag'); - handle.classList.add('at-selection-handle-drag'); - dragState.isDragging = type; - }, - false - ); - document.addEventListener( - 'mousemove', - e => { - if (dragState.isDragging !== type) { - return; - } - e.preventDefault(); - onMove(e); - }, - true - ); - document.addEventListener( - 'mouseup', - e => { - if (dragState.isDragging !== type) { - return; - } - e.preventDefault(); - dragState.isDragging = undefined; - element.classList.remove('at-selection-handle-drag'); - handle.classList.remove('at-selection-handle-drag'); - onDragEnd(e); - }, - true - ); -} - -function getRelativePosition(parent: HTMLElement, e: MouseEvent): { relX: number; relY: number } { - const parentPos = parent.getBoundingClientRect(); - const parentLeft: number = parentPos.left + parent.ownerDocument!.defaultView!.pageXOffset; - const parentTop: number = parentPos.top + parent.ownerDocument!.defaultView!.pageYOffset; - - const relX = e.pageX - parentLeft; - const relY = e.pageY - parentTop; - - return { relX, relY }; -} - -function getBeatFromEvent( - element: HTMLElement, - api: alphaTab.AlphaTabApi, - e: MouseEvent -): alphaTab.model.Beat | undefined { - const { relX, relY } = getRelativePosition(element, e); - const beat = api.boundsLookup?.getBeatAtPos(relX, relY); - if (!beat) { - return undefined; - } - - const bounds = api.boundsLookup!.findBeat(beat); - if (!bounds) { - return undefined; - } - - // only snap to beat beat if we are over the whitespace after the beat - const visualBoundsEnd = bounds.visualBounds.x + bounds.visualBounds.w; - const realBoundsEnd = bounds.realBounds.x + bounds.realBounds.w; - if (relX < visualBoundsEnd || relX > realBoundsEnd) { - return undefined; - } - - return beat; -} - -export function setupSelectionHandles(element: HTMLElement, api: alphaTab.AlphaTabApi) { - const { startHandle, endHandle } = createSelectionHandles(element); - - // listen to selection range changes to place handles - let currentHighlight: alphaTab.PlaybackHighlightChangeEventArgs | undefined; - api.playbackRangeHighlightChanged.on(e => { - currentHighlight = e; - // no selection - if (!e.startBeat || !e.endBeat) { - startHandle.classList.remove('active'); - endHandle.classList.remove('active'); - return; - } - - startHandle.classList.add('active'); - startHandle.style.left = `${e.startBeatBounds!.realBounds.x}px`; - startHandle.style.top = `${e.startBeatBounds!.barBounds.masterBarBounds.visualBounds.y}px`; - startHandle.style.height = `${e.startBeatBounds!.barBounds.masterBarBounds.visualBounds.h}px`; - - endHandle.classList.add('active'); - endHandle.style.left = `${e.endBeatBounds!.realBounds.x + e.endBeatBounds!.realBounds.w}px`; - endHandle.style.top = `${e.endBeatBounds!.barBounds.masterBarBounds.visualBounds.y}px`; - endHandle.style.height = `${e.endBeatBounds!.barBounds.masterBarBounds.visualBounds.h}px`; - }); - - // setup dragging of handles - const dragState: HandleDragState = { isDragging: undefined }; - - setupHandleDrag( - element, - startHandle, - dragState, - 'start', - e => { - if (!currentHighlight?.startBeat) { - return; - } - - const beat = getBeatFromEvent(element, api, e); - if (!beat) { - return; - } - - api.highlightPlaybackRange(beat, currentHighlight.endBeat!); - }, - () => { - api.applyPlaybackRangeFromHighlight(); - } - ); - - setupHandleDrag( - element, - endHandle, - dragState, - 'end', - e => { - if (!currentHighlight?.startBeat) { - return; - } - - const beat = getBeatFromEvent(element, api, e); - if (!beat) { - return; - } - - api.highlightPlaybackRange(currentHighlight!.startBeat!, beat); - }, - () => { - api.applyPlaybackRangeFromHighlight(); - } - ); -} diff --git a/packages/playground/src/apps/AlphaTexEditorApp.ts b/packages/playground/src/apps/AlphaTexEditorApp.ts new file mode 100644 index 000000000..ffbc74530 --- /dev/null +++ b/packages/playground/src/apps/AlphaTexEditorApp.ts @@ -0,0 +1,226 @@ +import * as alphaTab from '@coderline/alphatab'; +import Split from 'split.js'; +import { Footer } from '../components/Footer'; +import { LoadingOverlay } from '../components/LoadingOverlay'; +import { MonacoEditor } from '../components/alphatex-editor/MonacoEditor'; +import { NavMenu } from '../components/NavMenu'; +import { Sidebar } from '../components/Sidebar'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Paths } from '../util/Paths'; + +injectStyles( + 'AlphaTexEditorApp', + css` + .at-tex-app { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-direction: row; + } + .at-tex-app > .at-tex-editor-pane { + height: 100%; + overflow: visible; + z-index: 10000; + } + .at-tex-app > .at-tex-player-pane { + height: 100%; + overflow: hidden; + } + .at-tex-app .at-wrap-tex { + position: relative; + width: 100%; + height: 100%; + margin: 0; + background: #fff; + display: flex; + flex-direction: column; + overflow: hidden; + } + .at-tex-app .at-wrap-tex > .at-content { + flex: 1 1 auto; + overflow: hidden; + position: relative; + } + .at-tex-app .at-wrap-tex .at-viewport { + overflow-y: auto; + position: absolute; + top: 0; + left: 70px; + right: 0; + bottom: 0; + padding-right: 20px; + } + .at-tex-app .gutter { + background-color: #eee; + background-repeat: no-repeat; + background-position: 50%; + } + .at-tex-app .gutter.gutter-horizontal { + cursor: col-resize; + } +` +); + +const DEFAULT_TEX = `\\title "Canon Rock" +\\subtitle "JerryC" +\\tempo 90 + +:2 19.2{v f} 17.2{v f} | +15.2{v f} 14.2{v f}| +12.2{v f} 10.2{v f}| +12.2{v f} 14.2{v f}.4 :8 15.2 17.2 | +14.1.2 :8 17.2 15.1 14.1{h} 17.2 | +15.2{v d}.4 :16 17.2{h} 15.2 :8 14.2 14.1 17.1{b(0 4 4 0)}.4 | +15.1.8 :16 14.1{tu 3} 15.1{tu 3} 14.1{tu 3} :8 17.2 15.1 14.1 :16 12.1{tu 3} 14.1{tu 3} 12.1{tu 3} :8 15.2 14.2 | +12.2 14.3 12.3 15.2 :32 14.2{h} 15.2{h} 14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}`; + +const STORAGE_KEY = 'alphatex-editor.code'; + +export interface AlphaTexEditorAppOptions { + initialTex?: string; +} + +export class AlphaTexEditorApp implements Mountable { + readonly root: HTMLElement; + readonly api: alphaTab.AlphaTabApi; + private nav: NavMenu; + private overlay: LoadingOverlay; + private sidebar: Sidebar; + private footer: Footer; + private editor: MonacoEditor; + private split: ReturnType | null = null; + private fromTex = true; + private subscriptions: (() => void)[] = []; + + constructor(options: AlphaTexEditorAppOptions = {}) { + this.root = parseHtml(html` +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ `); + + const viewport = this.root.querySelector('.at-viewport')!; + const canvas = this.root.querySelector('.at-canvas')!; + + const settings = new alphaTab.Settings(); + settings.fillFromJson({ + core: { + fontDirectory: Paths.fontDirectory + }, + player: { + playerMode: alphaTab.PlayerMode.EnabledAutomatic, + soundFont: Paths.soundFont, + scrollOffsetX: -10, + scrollOffsetY: -20, + scrollElement: viewport + } + } satisfies alphaTab.json.SettingsJson); + + this.api = new alphaTab.AlphaTabApi(canvas, settings); + this.subscriptions.push(this.api.error.on(e => console.error('alphaTab error', e))); + + this.api.settings.exporter.comments = true; + this.api.settings.exporter.indent = 2; + + this.subscriptions.push( + this.api.scoreLoaded.on(score => { + if (!this.fromTex) { + const exporter = new alphaTab.exporter.AlphaTexExporter(); + const tex = exporter.exportToString(score, this.api.settings); + this.editor.setValue(tex); + } + }) + ); + + this.overlay = mount(this.root, '.cmp-overlay', new LoadingOverlay(this.api)); + this.sidebar = mount(this.root, '.cmp-sidebar', new Sidebar(this.api)); + this.footer = mount( + this.root, + '.cmp-footer', + new Footer(this.api, { trackList: this.sidebar.trackList }) + ); + + const initialCode = + (typeof sessionStorage !== 'undefined' ? sessionStorage.getItem(STORAGE_KEY) : null) ?? + options.initialTex ?? + DEFAULT_TEX; + + this.editor = mount(this.root, '.cmp-editor', new MonacoEditor({ initialCode })); + this.editor.onChange = tex => this.loadTex(tex); + + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + + // Initial load and split-pane setup happen after the root is in the DOM. + // Defer to next tick so the caller has a chance to appendChild before Split() measures. + queueMicrotask(() => this.afterMount(initialCode)); + + if (typeof window !== 'undefined') { + window.api = this.api; + window.alphaTab = alphaTab; + } + } + + private afterMount(initialCode: string): void { + if (!this.root.isConnected) { + queueMicrotask(() => this.afterMount(initialCode)); + return; + } + const editorPane = this.root.querySelector('.at-tex-editor-pane')!; + const playerPane = this.root.querySelector('.at-tex-player-pane')!; + this.split = Split([editorPane, playerPane]); + this.loadTex(initialCode); + } + + private loadTex(tex: string): void { + const importer = new alphaTab.importer.AlphaTexImporter(); + importer.initFromString(tex, this.api.settings); + importer.logErrors = true; + let score: alphaTab.model.Score; + try { + score = importer.readScore(); + this.api.updateSettings(); + } catch { + return; + } + + if (typeof sessionStorage !== 'undefined') { + sessionStorage.setItem(STORAGE_KEY, tex); + } + this.fromTex = true; + this.api.renderTracks(score.tracks, { reuseViewport: true }); + this.fromTex = false; + } + + dispose(): void { + for (const u of this.subscriptions) { + u(); + } + this.subscriptions = []; + this.split?.destroy(); + this.nav.dispose(); + this.editor.dispose(); + this.footer.dispose(); + this.sidebar.dispose(); + this.overlay.dispose(); + this.api.destroy(); + this.root.remove(); + } +} diff --git a/packages/playground/src/apps/ControlApp.ts b/packages/playground/src/apps/ControlApp.ts new file mode 100644 index 000000000..d3af97c6a --- /dev/null +++ b/packages/playground/src/apps/ControlApp.ts @@ -0,0 +1,173 @@ +import * as alphaTab from '@coderline/alphatab'; +import { Crosshair } from '../components/Crosshair'; +import { DragDrop } from '../components/DragDrop'; +import { Footer } from '../components/Footer'; +import { LoadingOverlay } from '../components/LoadingOverlay'; +import { NavMenu } from '../components/NavMenu'; +import { SelectionHandles } from '../components/SelectionHandles'; +import { Sidebar } from '../components/Sidebar'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Paths } from '../util/Paths'; + +injectStyles( + 'ControlApp', + css` + .at-wrap { + position: relative; + width: 90vw; + height: 90vh; + margin: 0 auto; + border: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + display: flex; + flex-direction: column; + overflow: hidden; + } + .at-wrap > .at-content { + flex: 1 1 auto; + overflow: hidden; + position: relative; + } + .at-wrap .at-viewport { + overflow-y: auto; + position: absolute; + top: 0; + left: 70px; + right: 0; + bottom: 0; + padding-right: 20px; + } + @media screen and (max-width: 1100px) { + .at-wrap .at-viewport { left: 0; } + } + @media screen and (max-width: 920px) { + .at-wrap { height: 60vh; } + } +` +); + +export interface ControlAppOptions { + file?: string; + soundFont?: string; + fontDirectory?: string; + settings?: alphaTab.json.SettingsJson; +} + +function applyFonts(settings: alphaTab.Settings): void { + settings.display.resources.copyrightFont.families = ['Noto Sans']; + settings.display.resources.titleFont.families = ['Noto Serif']; + settings.display.resources.subTitleFont.families = ['Noto Serif']; + settings.display.resources.wordsFont.families = ['Noto Serif']; + settings.display.resources.effectFont.families = ['Noto Serif']; + settings.display.resources.timerFont.families = ['Noto Serif']; + settings.display.resources.fretboardNumberFont.families = ['Noto Sans']; + settings.display.resources.tablatureFont.families = ['Noto Sans']; + settings.display.resources.graceFont.families = ['Noto Sans']; + settings.display.resources.barNumberFont.families = ['Noto Sans']; + settings.display.resources.markerFont.families = ['Noto Serif']; + settings.display.resources.directionsFont.families = ['Noto Serif']; + settings.display.resources.numberedNotationFont.families = ['Noto Sans']; + settings.display.resources.numberedNotationGraceFont.families = ['Noto Sans']; +} + +export function buildSettings(options: ControlAppOptions, viewport: HTMLElement): alphaTab.Settings { + const params = new URL(window.location.href).searchParams; + const settings = new alphaTab.Settings(); + applyFonts(settings); + settings.fillFromJson({ + core: { + logLevel: (params.get('loglevel') ?? 'info') as alphaTab.json.CoreSettingsJson['logLevel'], + engine: params.get('engine') ?? 'default', + file: options.file ?? Paths.defaultScore, + fontDirectory: options.fontDirectory ?? Paths.fontDirectory + }, + player: { + playerMode: alphaTab.PlayerMode.EnabledAutomatic, + scrollOffsetX: -10, + scrollOffsetY: -20, + soundFont: options.soundFont ?? Paths.soundFont, + scrollElement: viewport + } + } satisfies alphaTab.json.SettingsJson); + if (options.settings) { + settings.fillFromJson(options.settings); + } + return settings; +} + +export class ControlApp implements Mountable { + readonly root: HTMLElement; + readonly api: alphaTab.AlphaTabApi; + readonly sidebar: Sidebar; + readonly footer: Footer; + private overlay: LoadingOverlay; + private selectionHandles: SelectionHandles; + private crosshair: Crosshair; + private dragDrop: DragDrop; + private nav: NavMenu; + private unsubError: () => void; + + constructor(options: ControlAppOptions = {}) { + this.root = parseHtml(html` +
+
+
+
+
+
+
+
+ +
+
+ `); + + const viewport = this.root.querySelector('.at-viewport')!; + const canvas = this.root.querySelector('.at-canvas')!; + const settings = buildSettings(options, viewport); + + this.api = new alphaTab.AlphaTabApi(canvas, settings); + this.unsubError = this.api.error.on(e => { + console.error('alphaTab error', e); + }); + + this.overlay = mount(this.root, '.cmp-overlay', new LoadingOverlay(this.api)); + this.sidebar = mount(this.root, '.cmp-sidebar', new Sidebar(this.api)); + this.footer = mount( + this.root, + '.cmp-footer', + new Footer(this.api, { trackList: this.sidebar.trackList }) + ); + this.selectionHandles = mount( + this.root, + '.cmp-selection-handles', + new SelectionHandles(this.api, viewport) + ); + this.crosshair = new Crosshair(); + this.dragDrop = new DragDrop(this.api, { + onEnter: () => this.overlay.enterDrag(), + onLeave: () => this.overlay.leaveDrag() + }); + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + + // expose for fiddling in dev tools + if (typeof window !== 'undefined') { + window.api = this.api; + window.alphaTab = alphaTab; + } + } + + dispose(): void { + this.unsubError(); + this.nav.dispose(); + this.dragDrop.dispose(); + this.crosshair.dispose(); + this.selectionHandles.dispose(); + this.footer.dispose(); + this.sidebar.dispose(); + this.overlay.dispose(); + this.api.destroy(); + this.root.remove(); + } +} diff --git a/packages/playground/src/apps/LabsIndexApp.ts b/packages/playground/src/apps/LabsIndexApp.ts new file mode 100644 index 000000000..674768ed3 --- /dev/null +++ b/packages/playground/src/apps/LabsIndexApp.ts @@ -0,0 +1,92 @@ +import { NavMenu } from '../components/NavMenu'; +import { DEMOS } from '../util/Demos'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; + +injectStyles( + 'LabsIndexApp', + css` + .at-labs { + max-width: 980px; + margin: 0 auto; + padding: 32px 24px; + } + .at-labs h1 { + margin-top: 0; + font-family: 'Noto Serif', serif; + } + .at-labs > p { color: #444; } + .at-labs-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; + margin-top: 24px; + } + .at-labs-card { + display: flex; + flex-direction: column; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + padding: 18px 20px; + background: #fff; + text-decoration: none; + color: inherit; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + .at-labs-card:hover { + border-color: var(--at-accent); + box-shadow: 0 4px 14px rgba(73, 114, 161, 0.18); + } + .at-labs-card-title { + font-weight: 500; + margin: 0 0 8px 0; + font-size: 1.05rem; + } + .at-labs-card-description { + margin: 0; + color: #555; + font-size: 0.9rem; + line-height: 1.4; + } + .at-labs-footnote { + margin-top: 32px; + font-size: 0.85rem; + color: #777; + } +` +); + +export class LabsIndexApp implements Mountable { + readonly root: HTMLElement; + private nav: NavMenu; + + constructor() { + this.root = parseHtml(html` +
+

alphaTab Playground

+

Development demos exercising specific capabilities of alphaTab. Pick one to start.

+
+

+ Editing alphaTab source under packages/alphatab/src/ hot-reloads via Vite's workspace alias. +

+
+ `); + const grid = this.root.querySelector('.at-labs-grid')!; + for (const demo of DEMOS) { + const card = parseHtml(html` + +

${demo.title}

+

${demo.description}

+
+ `); + grid.appendChild(card); + } + + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + } + + dispose(): void { + this.nav.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/apps/RecorderApp.ts b/packages/playground/src/apps/RecorderApp.ts new file mode 100644 index 000000000..7a10eee64 --- /dev/null +++ b/packages/playground/src/apps/RecorderApp.ts @@ -0,0 +1,354 @@ +import * as alphaTab from '@coderline/alphatab'; +import { Footer } from '../components/Footer'; +import { LoadingOverlay } from '../components/LoadingOverlay'; +import { NavMenu } from '../components/NavMenu'; +import { Sidebar } from '../components/Sidebar'; +import { DrumPadPanel } from '../components/recorder/DrumPadPanel'; +import { type RhythmConfig, RHYTHM_4_4_STRAIGHT } from '../components/recorder/RhythmConfig'; +import { RhythmGridOverlay } from '../components/recorder/RhythmGridOverlay'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Paths } from '../util/Paths'; + +injectStyles( + 'RecorderApp', + css` + .at-wrap.at-wrap-recorder { + position: relative; + width: 90vw; + height: 90vh; + margin: 0 auto; + border: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + display: flex; + flex-direction: column; + overflow: hidden; + } + .at-wrap-recorder > .at-content { + flex: 1 1 auto; + overflow: hidden; + position: relative; + } + .at-wrap-recorder .at-viewport { + overflow-y: auto; + position: absolute; + top: 0; + left: 70px; + right: 0; + bottom: 0; + padding-right: 20px; + } + .at-wrap-recorder .at-canvas { + position: relative; + } + @media screen and (max-width: 1100px) { + .at-wrap-recorder .at-viewport { left: 0; } + } +` +); + +const MIDI_QUARTER_TIME = 960; + +export interface RecorderAppOptions { + rhythm?: RhythmConfig; + fontDirectory?: string; + soundFont?: string; +} + +export class RecorderApp implements Mountable { + readonly api: alphaTab.AlphaTabApi; + readonly root: HTMLElement; + + private rhythm: RhythmConfig; + private slotDuration: alphaTab.model.Duration; + private slotTicks: number; + private score: alphaTab.model.Score; + private track: alphaTab.model.Track; + private staff: alphaTab.model.Staff; + private currentBeat: alphaTab.model.Beat | undefined; + private insertTickThreshold = -1; + private wasPlaying = false; + + private overlay: LoadingOverlay; + private sidebar: Sidebar; + private footer: Footer; + private gridOverlay: RhythmGridOverlay; + private padPanel: DrumPadPanel; + private nav: NavMenu; + + private subscriptions: (() => void)[] = []; + + constructor(options: RecorderAppOptions = {}) { + this.rhythm = options.rhythm ?? RHYTHM_4_4_STRAIGHT; + this.slotDuration = pickSlotDuration(this.rhythm.subdivisionsPerBeat); + this.slotTicks = MIDI_QUARTER_TIME / this.rhythm.subdivisionsPerBeat; + + this.root = parseHtml(html` +
+
+
+
+
+
+
+
+ +
+ `); + + const viewport = this.root.querySelector('.at-viewport')!; + const canvas = this.root.querySelector('.at-canvas')!; + + const settings = new alphaTab.Settings(); + settings.fillFromJson({ + core: { + fontDirectory: options.fontDirectory ?? Paths.fontDirectory + }, + player: { + playerMode: alphaTab.PlayerMode.EnabledAutomatic, + soundFont: options.soundFont ?? Paths.soundFont, + scrollOffsetX: -10, + scrollOffsetY: -20, + scrollElement: viewport + }, + display: { + layoutMode: alphaTab.LayoutMode.Parchment, + justifyLastSystem: true + } + } satisfies alphaTab.json.SettingsJson); + + this.api = new alphaTab.AlphaTabApi(canvas, settings); + this.subscriptions.push(this.api.error.on(e => console.error('alphaTab error', e))); + + // build the empty percussion score + this.score = new alphaTab.model.Score(); + this.track = new alphaTab.model.Track(); + this.track.name = 'Drums'; + this.track.shortName = 'Drums'; + this.track.ensureStaveCount(1); + this.track.playbackInfo.primaryChannel = 9; + this.track.playbackInfo.secondaryChannel = 9; + this.staff = this.track.staves[0]; + this.staff.isPercussion = true; + this.staff.showTablature = false; + this.staff.showStandardNotation = true; + this.score.addTrack(this.track); + this.score.defaultSystemsLayout = 5; + this.track.defaultSystemsLayout = 5; + + // mount overlay/sidebar/footer + this.overlay = mount(this.root, '.cmp-overlay', new LoadingOverlay(this.api)); + this.sidebar = mount(this.root, '.cmp-sidebar', new Sidebar(this.api)); + this.footer = mount( + this.root, + '.cmp-footer', + new Footer(this.api, { trackList: this.sidebar.trackList }) + ); + + // grid overlay sits inside .at-canvas — alphaTab adds its rendering as siblings/children. + this.gridOverlay = new RhythmGridOverlay(this.api, this.rhythm); + canvas.appendChild(this.gridOverlay.root); + + // drum pad panel: floating on body + this.padPanel = new DrumPadPanel(); + this.padPanel.onHit = note => this.addDrumHit(note); + document.body.appendChild(this.padPanel.root); + + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + + // wire api events + this.subscriptions.push(this.api.scoreLoaded.on(() => this.updateInsertTickThreshold())); + this.subscriptions.push( + this.api.midiLoad.on(midi => { + this.extendMidi(midi); + }) + ); + this.subscriptions.push( + this.api.playerPositionChanged.on(e => { + if (this.insertTickThreshold !== -1 && e.currentTick >= this.insertTickThreshold) { + this.insertNewSystem(); + } + }) + ); + this.subscriptions.push( + this.api.playedBeatChanged.on(beat => { + this.currentBeat = beat; + }) + ); + this.subscriptions.push( + this.api.playerStateChanged.on(e => { + const isPlaying = e.state === alphaTab.synth.PlayerState.Playing; + if (this.wasPlaying && !isPlaying) { + // Regenerate midi so just-recorded drums become audible on replay. + this.api.loadMidiForScore(); + } + this.wasPlaying = isPlaying; + this.padPanel.setRecording(isPlaying); + }) + ); + + // seed initial system + this.insertNewSystem(); + + if (typeof window !== 'undefined') { + window.api = this.api; + window.alphaTab = alphaTab; + } + } + + private addDrumHit(midiNote: number): void { + if (this.api.playerState !== alphaTab.synth.PlayerState.Playing || !this.currentBeat) { + return; + } + const note = new alphaTab.model.Note(); + note.percussionArticulation = midiNote; + this.currentBeat.addNote(note); + this.currentBeat.isEmpty = false; + + const bar = this.currentBeat.voice.bar; + bar.finish(this.api.settings, null); + + this.api.renderScore(this.score, undefined, { + reuseViewport: true, + firstChangedMasterBar: bar.index + }); + + this.padPanel.flash(midiNote); + } + + private createEmptyBeatsForBar(): alphaTab.model.Beat[] { + const beats: alphaTab.model.Beat[] = []; + for (let i = 0; i < this.rhythm.beatMask.length; i++) { + const beat = new alphaTab.model.Beat(); + beat.duration = this.slotDuration; + beat.isEmpty = true; + beats.push(beat); + } + return beats; + } + + private insertNewMasterBar(): alphaTab.model.MasterBar { + const masterBar = new alphaTab.model.MasterBar(); + masterBar.timeSignatureNumerator = this.rhythm.timeSignatureNumerator; + masterBar.timeSignatureDenominator = this.rhythm.timeSignatureDenominator; + this.score.addMasterBar(masterBar); + + const lookup = new alphaTab.midi.MasterBarTickLookup(); + lookup.tempoChanges.push(new alphaTab.midi.MasterBarTickLookupTempoChange(masterBar.start, this.score.tempo)); + lookup.start = masterBar.start; + lookup.end = masterBar.start + masterBar.calculateDuration(); + lookup.masterBar = masterBar; + this.api.tickCache?.addMasterBar(lookup); + return masterBar; + } + + private insertNewBar(): alphaTab.model.Bar { + const previousBar = this.staff.bars.length > 0 ? this.staff.bars[this.staff.bars.length - 1] : null; + const bar = new alphaTab.model.Bar(); + if (previousBar) { + bar.clef = previousBar.clef; + bar.clefOttava = previousBar.clefOttava; + bar.keySignature = previousBar.keySignature; + bar.keySignatureType = previousBar.keySignatureType; + } + this.staff.addBar(bar); + const voice = new alphaTab.model.Voice(); + bar.addVoice(voice); + + const beats = this.createEmptyBeatsForBar(); + for (let i = 0; i < beats.length; i++) { + voice.addBeat(beats[i]); + this.api.tickCache?.addBeat(beats[i], i * this.slotTicks, this.slotTicks); + } + return bar; + } + + private insertNewSystem(): void { + this.insertTickThreshold = -1; + const currentSystemCount = Math.floor(this.score.masterBars.length / this.score.defaultSystemsLayout); + const neededSystemCount = currentSystemCount + 1; + const neededBars = neededSystemCount * this.score.defaultSystemsLayout; + const lastMasterBarIndex = this.score.masterBars.length - 1; + + let missing = neededBars - this.score.masterBars.length; + while (missing > 0) { + const masterBar = this.insertNewMasterBar(); + const bar = this.insertNewBar(); + const dataBag = new Map(); + masterBar.finish(dataBag); + bar.finish(this.api.settings, dataBag); + missing--; + } + + this.updateInsertTickThreshold(); + this.api.renderScore(this.score, undefined, { + reuseViewport: currentSystemCount > 0, + firstChangedMasterBar: currentSystemCount > 0 ? lastMasterBarIndex : undefined + }); + } + + private updateInsertTickThreshold(): void { + if (this.score.masterBars.length < 2) { + this.insertTickThreshold = -1; + return; + } + const lastBar = this.score.masterBars[this.score.masterBars.length - 2]; + const thresholdPercent = 0.8; + this.insertTickThreshold = lastBar.start + lastBar.calculateDuration() * thresholdPercent; + } + + private extendMidi(midi: alphaTab.midi.MidiFile): void { + let rest: alphaTab.midi.AlphaTabRestEvent | undefined; + const events = midi.tracks[0].events; + for (let i = events.length - 1; i >= 0; i--) { + const e = events[i]; + if (e instanceof alphaTab.midi.AlphaTabRestEvent) { + rest = e; + break; + } + } + if (!rest) { + return; + } + const desiredMs = 60 * 30 * 1000; // 30 min + const tempo = this.api.tickCache!.masterBars[0].tempoChanges[0].tempo; + const desiredTicks = (desiredMs / (60000.0 / (tempo * MIDI_QUARTER_TIME))) | 0; + const endOfTrack = events.pop()! as alphaTab.midi.EndOfTrackEvent; + let tick = rest.tick + MIDI_QUARTER_TIME; + while (tick < desiredTicks) { + events.push(new alphaTab.midi.AlphaTabRestEvent(rest.track, tick, rest.channel)); + tick += MIDI_QUARTER_TIME; + } + endOfTrack.tick = tick; + } + + dispose(): void { + for (const u of this.subscriptions) { + u(); + } + this.subscriptions = []; + this.nav.dispose(); + this.padPanel.dispose(); + this.gridOverlay.dispose(); + this.footer.dispose(); + this.sidebar.dispose(); + this.overlay.dispose(); + this.api.destroy(); + this.root.remove(); + } +} + +function pickSlotDuration(subdivisionsPerBeat: number): alphaTab.model.Duration { + switch (subdivisionsPerBeat) { + case 2: + return alphaTab.model.Duration.Eighth; + case 4: + return alphaTab.model.Duration.Sixteenth; + case 8: + return alphaTab.model.Duration.ThirtySecond; + case 16: + return alphaTab.model.Duration.SixtyFourth; + default: + throw new Error(`unsupported subdivisionsPerBeat: ${subdivisionsPerBeat}`); + } +} diff --git a/packages/playground/src/apps/TestResultsApp.ts b/packages/playground/src/apps/TestResultsApp.ts new file mode 100644 index 000000000..35da520b2 --- /dev/null +++ b/packages/playground/src/apps/TestResultsApp.ts @@ -0,0 +1,402 @@ +import { ByteBuffer } from '@coderline/alphatab/io/ByteBuffer'; +import { ZipReader } from '@coderline/alphatab/zip/ZipReader'; +import { NavMenu } from '../components/NavMenu'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; + +injectStyles( + 'TestResultsApp', + css` + body { + justify-content: flex-start; + } + body > * { + overflow: visible; + } + .at-test-results { + padding: 1rem; + font-family: 'Noto Sans', sans-serif; + min-height: 100vh; + max-width: 90vw; + } + .at-test-results > h1 { margin-top: 0; } + .at-test-results-toolbar { margin: 1rem 0; } + .at-test-results-list .at-test-card { + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 4px; + margin: 1rem 0; + padding: 12px; + } + .at-test-results-list .at-test-card.accepted { border-color: green; } + .at-test-results-list .at-test-card-title { + font-weight: 500; + font-size: 1rem; + margin: 0 0 8px 0; + } + .at-test-comparer { position: relative; } + .at-test-comparer .slider-handle { + position: absolute; + bottom: 0; + width: 40px; + transform: translateX(-50%); + cursor: ew-resize; + z-index: 10; + touch-action: none; + user-select: none; + } + .at-test-comparer .slider-handle::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 2px; + transform: translateX(-50%); + background: rgba(255, 255, 255, 0.9); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 0 4px rgba(0, 0, 0, 0.15); + pointer-events: none; + } + .at-test-comparer .slider-handle::after { + content: ''; + position: sticky; + top: calc(50vh - 20px); + display: block; + width: 40px; + height: 40px; + margin-top: var(--knob-margin-top, 0); + background-color: #fff; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 18L3 12l6-6M15 6l6 6-6 6' fill='none' stroke='%23555' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: 22px; + border-radius: 50%; + border: 1.5px solid rgba(0, 0, 0, 0.15); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.06); + pointer-events: none; + } + .at-test-comparer .slider-handle:hover::after { + box-shadow: 0 3px 12px rgba(0, 0, 0, 0.35), 0 0 0 1.5px rgba(0, 0, 0, 0.12); + } + .at-test-comparer .expected, + .at-test-comparer .actual, + .at-test-comparer .diff { + background: #fff; + border: 1px solid red; + position: absolute; + } + .at-test-comparer .expected { left: 0; } + .at-test-comparer .actual { + right: -2px; + box-shadow: -7px 0 10px -5px rgba(0, 0, 0, 0.5); + overflow: hidden; + border-left: 0; + } + .at-test-comparer .actual img { + position: absolute; + right: 0; + top: 0; + border-left: 1px solid red; + } + .at-test-comparer .diff { + display: none; + left: 0; + } + .at-test-card.accepted .diff, + .at-test-card.accepted .expected, + .at-test-card.accepted .actual { border-color: green; } + body.hide-accepted .at-test-card.accepted { display: none; } + + .at-test-controls { + position: sticky; + top: 0; + z-index: 20; + background: #fff; + padding: 6px 0; + display: flex; + gap: 12px; + align-items: center; + } + .at-test-controls .btn { + padding: 4px 10px; + background: #6c757d; + border: 0; + color: #fff; + font: inherit; + cursor: pointer; + border-radius: 4px; + } + .at-test-controls .btn[disabled] { opacity: 0.5; cursor: default; } + + .drop-area { + position: fixed; + inset: 0; + background: rgba(255, 255, 255, 0.5); + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease-in-out; + } + body.drop .drop-area { opacity: 1; } + .drop-message { + width: 200px; + height: 200px; + font-weight: bold; + border: 5px dashed #426d9d; + color: #426d9d; + display: flex; + text-align: center; + border-radius: 10px; + justify-content: center; + align-items: center; + padding: 1rem; + } + .at-test-no-failures { + padding: 12px; + background: #d1e7dd; + color: #0f5132; + border-radius: 4px; + } +` +); + +interface TestResult { + originalFile: string; + newFile: string | Uint8Array; + diffFile: string | Uint8Array; + accepted?: true; +} + +export class TestResultsApp implements Mountable { + readonly root: HTMLElement; + private listEl: HTMLElement; + private remainingEl: HTMLElement; + private currentResults: TestResult[] = []; + private nav: NavMenu; + + private onDragOver = (e: DragEvent) => { + e.stopPropagation(); + e.preventDefault(); + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'link'; + } + }; + private onDragEnter = () => document.body.classList.add('drop'); + private onDragLeave = () => document.body.classList.remove('drop'); + private onDrop = (e: DragEvent) => this.handleDrop(e); + + constructor() { + this.root = parseHtml(html` +
+

alphaTab — Visual Test Results

+

+ This page shows failing visual tests for review and acceptance. + Run npm run test to populate results, or drop a results zip onto the page. +

+
+ +
+
+
Drop test-results zip here
+
+ `); + this.listEl = this.root.querySelector('.at-test-results-list')!; + this.remainingEl = this.root.querySelector('.at-test-remaining')!; + + this.root.querySelector('.at-test-hide-accepted')!.onchange = e => { + document.body.classList.toggle('hide-accepted', (e.target as HTMLInputElement).checked); + }; + + document.body.addEventListener('dragover', this.onDragOver, false); + document.body.addEventListener('dragenter', this.onDragEnter, true); + document.body.addEventListener('dragleave', this.onDragLeave); + document.body.addEventListener('drop', this.onDrop, true); + + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + + this.loadFromServer(); + } + + private async loadFromServer(): Promise { + try { + const res = await fetch('/test-results/list'); + const list = (await res.json()) as TestResult[]; + this.displayResults(list); + } catch { + alert('error loading test results'); + } + } + + private updateRemaining(): void { + if (this.currentResults.length === 0) { + this.remainingEl.textContent = ''; + return; + } + const remaining = this.currentResults.filter(r => !r.accepted).length; + this.remainingEl.textContent = `(${remaining}/${this.currentResults.length})`; + } + + private async displayResults(results: TestResult[]): Promise { + this.listEl.replaceChildren(); + this.currentResults = results; + if (results.length === 0) { + const banner = parseHtml(html`
No reported errors on visual tests.
`); + this.listEl.appendChild(banner); + this.updateRemaining(); + return; + } + for (const result of results) { + this.listEl.appendChild(await this.createResultCard(result)); + } + this.updateRemaining(); + } + + private async createResultCard(result: TestResult): Promise { + const card = parseHtml(html` +
+
${result.originalFile}
+
+ + +
+
+
expected
+
actual
+
diff
+
+
+
+ `); + const comparer = card.querySelector('.at-test-comparer')!; + const ex = comparer.querySelector('.expected')!; + const ac = comparer.querySelector('.actual')!; + const df = comparer.querySelector('.diff')!; + const handle = comparer.querySelector('.slider-handle')!; + const exImg = ex.querySelector('img')!; + const acImg = ac.querySelector('img')!; + const dfImg = df.querySelector('img')!; + + await Promise.allSettled([ + loadImage(exImg, result.originalFile), + loadImage(acImg, result.newFile), + loadImage(dfImg, result.diffFile) + ]); + + const width = Math.max(exImg.width, acImg.width); + const height = Math.max(exImg.height, acImg.height); + comparer.style.width = `${width}px`; + comparer.style.height = `${height}px`; + ex.style.width = `${width}px`; + ex.style.height = `${height}px`; + ac.style.width = `${width / 2}px`; + ac.style.height = `${height}px`; + df.style.width = `${width}px`; + df.style.height = `${height}px`; + + handle.style.left = `${width / 2}px`; + handle.style.setProperty('--knob-margin-top', `${height / 2 - 20}px`); + + handle.addEventListener('pointerdown', e => { + handle.setPointerCapture(e.pointerId); + e.preventDefault(); + }); + handle.addEventListener('pointermove', e => { + if (!e.buttons) { return; } + const rect = comparer.getBoundingClientRect(); + const x = Math.max(0, Math.min(e.clientX - rect.left, width)); + handle.style.left = `${x}px`; + ac.style.width = `${width - x}px`; + }); + card.querySelector('.diff-toggle')!.onchange = e => { + df.style.display = (e.target as HTMLInputElement).checked ? 'block' : 'none'; + }; + const acceptBtn = card.querySelector('.accept')!; + acceptBtn.onclick = async () => { + acceptBtn.disabled = true; + acceptBtn.textContent = 'Accepting...'; + try { + const res = await fetch('/test-results/accept', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(result) + }); + const body = await res.json(); + acceptBtn.textContent = body.message ?? 'Accepted'; + card.classList.add('accepted'); + result.accepted = true; + this.updateRemaining(); + } catch { + alert('error accepting test result'); + acceptBtn.disabled = false; + acceptBtn.textContent = 'Accept'; + } + }; + return card; + } + + private handleDrop(e: DragEvent): void { + e.stopPropagation(); + e.preventDefault(); + document.body.classList.remove('drop'); + const files = e.dataTransfer?.files; + if (!files || files.length !== 1) { + return; + } + const reader = new FileReader(); + reader.onload = data => { + const buffer = data.target?.result; + if (!(buffer instanceof ArrayBuffer)) { + return; + } + const zip = new ZipReader(ByteBuffer.fromBuffer(new Uint8Array(buffer)), 128000000); + const entries = zip.read(); + const grouped = new Map(); + for (const entry of entries) { + if (entry.data.length === 0) { + continue; + } + const path = entry.fullName.startsWith('test-data/') ? entry.fullName : `test-data/${entry.fullName}`; + const key = `${path.replace('.diff.png', '').replace('.new.png', '')}.png`; + let result = grouped.get(key); + if (!result) { + result = { originalFile: key, newFile: '', diffFile: '' }; + grouped.set(key, result); + } + if (entry.fullName.endsWith('.diff.png')) { + result.diffFile = entry.data; + } else if (entry.fullName.endsWith('.new.png')) { + result.newFile = entry.data; + } + } + this.displayResults(Array.from(grouped.values())); + }; + reader.readAsArrayBuffer(files[0]); + } + + dispose(): void { + document.body.removeEventListener('dragover', this.onDragOver, false); + document.body.removeEventListener('dragenter', this.onDragEnter, true); + document.body.removeEventListener('dragleave', this.onDragLeave); + document.body.removeEventListener('drop', this.onDrop, true); + this.nav.dispose(); + this.root.remove(); + } +} + +function loadImage(img: HTMLImageElement, source: string | Uint8Array): Promise { + return new Promise((resolve, reject) => { + img.onload = () => resolve(); + img.onerror = () => reject(); + if (source instanceof Uint8Array) { + img.src = URL.createObjectURL(new Blob([source.buffer as ArrayBuffer], { type: 'image/png' })); + } else if (typeof source === 'string' && source.length > 0) { + img.src = `/${source}`; + } else { + reject(); + } + }); +} diff --git a/packages/playground/src/apps/YoutubeSyncApp.ts b/packages/playground/src/apps/YoutubeSyncApp.ts new file mode 100644 index 000000000..9f04c2f9e --- /dev/null +++ b/packages/playground/src/apps/YoutubeSyncApp.ts @@ -0,0 +1,136 @@ +import * as alphaTab from '@coderline/alphatab'; +import { Footer } from '../components/Footer'; +import { LoadingOverlay } from '../components/LoadingOverlay'; +import { NavMenu } from '../components/NavMenu'; +import { Sidebar } from '../components/Sidebar'; +import { YoutubeSync } from '../components/youtube-sync/YoutubeSync'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Paths } from '../util/Paths'; + +injectStyles( + 'YoutubeSyncApp', + css` + .at-yt-app { + display: flex; + flex-direction: column; + height: 100vh; + width: 100vw; + } + .at-yt-app > .cmp-yt { + flex: 0 0 auto; + } + .at-yt-app > .at-wrap-yt { + position: relative; + flex: 1 1 auto; + margin: 0; + background: #fff; + display: flex; + flex-direction: column; + overflow: hidden; + } + .at-yt-app > .at-wrap-yt > .at-content { + flex: 1 1 auto; + overflow: hidden; + position: relative; + } + .at-yt-app .at-viewport { + overflow-y: auto; + position: absolute; + top: 0; + left: 70px; + right: 0; + bottom: 0; + padding-right: 20px; + } +` +); + +export interface YoutubeSyncAppOptions { + videoId?: string; + file?: string; +} + +export class YoutubeSyncApp implements Mountable { + readonly root: HTMLElement; + readonly api: alphaTab.AlphaTabApi; + private nav: NavMenu; + private overlay: LoadingOverlay; + private sidebar: Sidebar; + private footer: Footer; + private youtube: YoutubeSync; + private subscriptions: (() => void)[] = []; + + constructor(options: YoutubeSyncAppOptions = {}) { + this.root = parseHtml(html` +
+
+
+
+
+
+
+
+
+
+ +
+
+ `); + + const viewport = this.root.querySelector('.at-viewport')!; + const canvas = this.root.querySelector('.at-canvas')!; + + const settings = new alphaTab.Settings(); + settings.fillFromJson({ + core: { + file: options.file ?? '/test-data/guitarpro8/canon-audio-track.gp', + fontDirectory: Paths.fontDirectory + }, + player: { + playerMode: alphaTab.PlayerMode.EnabledExternalMedia, + soundFont: Paths.soundFont, + scrollOffsetX: -10, + scrollOffsetY: -20, + scrollElement: viewport + } + } satisfies alphaTab.json.SettingsJson); + + this.api = new alphaTab.AlphaTabApi(canvas, settings); + this.subscriptions.push(this.api.error.on(e => console.error('alphaTab error', e))); + + this.overlay = mount(this.root, '.cmp-overlay', new LoadingOverlay(this.api)); + this.sidebar = mount(this.root, '.cmp-sidebar', new Sidebar(this.api)); + this.footer = mount( + this.root, + '.cmp-footer', + new Footer(this.api, { trackList: this.sidebar.trackList }) + ); + this.youtube = mount( + this.root, + '.cmp-yt', + new YoutubeSync(this.api, { videoId: options.videoId ?? 'by8oyJztzwo' }) + ); + + this.nav = new NavMenu(); + document.body.appendChild(this.nav.root); + + if (typeof window !== 'undefined') { + window.api = this.api; + window.alphaTab = alphaTab; + } + } + + dispose(): void { + for (const u of this.subscriptions) { + u(); + } + this.subscriptions = []; + this.nav.dispose(); + this.youtube.dispose(); + this.footer.dispose(); + this.sidebar.dispose(); + this.overlay.dispose(); + this.api.destroy(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/AudioExporter.ts b/packages/playground/src/components/AudioExporter.ts new file mode 100644 index 000000000..ca6f5eb11 --- /dev/null +++ b/packages/playground/src/components/AudioExporter.ts @@ -0,0 +1,103 @@ +import * as alphaTab from '@coderline/alphatab'; +import { Paths } from '../util/Paths'; +import type { TrackItem } from './TrackItem'; + +function downloadBlob(blob: Blob, filename: string): void { + const a = document.createElement('a'); + a.download = filename; + a.href = URL.createObjectURL(blob); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(a.href); +} + +export function exportGp7(api: alphaTab.AlphaTabApi): void { + if (!api.score) { + return; + } + const exporter = new alphaTab.exporter.Gp7Exporter(); + const data = exporter.export(api.score, api.settings); + const filename = api.score.title.length > 0 ? `${api.score.title}.gp` : 'song.gp'; + downloadBlob(new Blob([data as Uint8Array]), filename); +} + +async function fetchSoundFont(url: string): Promise { + const res = await fetch(url); + if (!res.ok) { + throw new Error(`Failed to load soundfont '${url}': ${res.status} ${res.statusText}`); + } + return new Uint8Array(await res.arrayBuffer()); +} + +export async function exportAudio(api: alphaTab.AlphaTabApi, trackItems: readonly TrackItem[] = []): Promise { + if (!api.score) { + return; + } + const options = new alphaTab.synth.AudioExportOptions(); + options.sampleRate = 44100; + options.masterVolume = api.masterVolume; + options.metronomeVolume = api.metronomeVolume; + if (api.playbackRange) { + options.playbackRange = api.playbackRange; + } + + const soloed = new Set(); + for (const item of trackItems) { + const idx = item.track.index; + const ratio = item.getVolume() / Math.max(1, item.track.playbackInfo.volume); + options.trackVolume.set(idx, item.isMuted() ? 0 : ratio); + if (item.isSoloed()) { + soloed.add(idx); + } + } + if (soloed.size > 0) { + for (const t of api.score.tracks) { + if (!soloed.has(t.index)) { + options.trackVolume.set(t.index, 0); + } + } + } + + options.soundFonts = [await fetchSoundFont(Paths.soundFont)]; + + const exporter = await api.exportAudio(options); + let generated: Float32Array | undefined; + let totalSamples = 0; + try { + while (true) { + const chunk = await exporter.render(500); + if (!chunk) { + break; + } + if (!generated) { + generated = new Float32Array(options.sampleRate * (chunk.endTime / 1000) * 2); + } + const needed = totalSamples + chunk.samples.length; + if (generated.length < needed) { + const grown = new Float32Array(needed); + grown.set(generated, 0); + generated = grown; + } + generated.set(chunk.samples, totalSamples); + totalSamples += chunk.samples.length; + } + } finally { + exporter.destroy(); + } + + if (!generated) { + return; + } + if (totalSamples < generated.length) { + generated = generated.subarray(0, totalSamples); + } + const filename = + api.score.title.length > 0 + ? `${api.score.title}_${options.sampleRate}_float32.pcm` + : `song_${options.sampleRate}_float32.pcm`; + const blob = new Blob([ + new Uint8Array(generated.buffer as ArrayBuffer, generated.byteOffset, generated.byteLength) + ]); + downloadBlob(blob, filename); +} diff --git a/packages/playground/src/components/Crosshair.ts b/packages/playground/src/components/Crosshair.ts new file mode 100644 index 000000000..c1c0bd207 --- /dev/null +++ b/packages/playground/src/components/Crosshair.ts @@ -0,0 +1,96 @@ +import { css, injectStyles } from '../util/Dom'; + +injectStyles( + 'Crosshair', + css` + .at-crosshair { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + pointer-events: none; + z-index: 20000; + } + .at-crosshair-x, + .at-crosshair-y { + position: absolute; + pointer-events: none; + } + .at-crosshair-y { + width: 100vw; + border-bottom: 1px dotted #000; + height: 1px; + left: 0; + } + .at-crosshair-x { + height: 100vh; + border-left: 1px dotted #000; + width: 1px; + top: 0; + } +` +); + +/** + * Behaviour-only debug helper: while CapsLock is on, draws a full-viewport crosshair + * that follows the mouse. No DOM until activated; cleaned up when CapsLock turns off. + */ +export class Crosshair { + private root: HTMLElement | null = null; + private xLine: HTMLElement | null = null; + private yLine: HTMLElement | null = null; + private active = false; + + private onKeyDown = (e: KeyboardEvent) => { + const should = e.getModifierState('CapsLock'); + if (should !== this.active) { + if (should) { + this.show(); + } else { + this.hide(); + } + } + }; + + private onMouseMove = (e: MouseEvent) => { + if (this.xLine) { + this.xLine.style.left = `${e.pageX}px`; + } + if (this.yLine) { + this.yLine.style.top = `${e.pageY}px`; + } + }; + + constructor() { + document.addEventListener('keydown', this.onKeyDown); + } + + private show(): void { + this.active = true; + this.root = document.createElement('div'); + this.root.className = 'at-crosshair'; + this.xLine = document.createElement('div'); + this.xLine.className = 'at-crosshair-x'; + this.yLine = document.createElement('div'); + this.yLine.className = 'at-crosshair-y'; + this.root.appendChild(this.xLine); + this.root.appendChild(this.yLine); + document.body.appendChild(this.root); + document.addEventListener('mousemove', this.onMouseMove, true); + } + + private hide(): void { + this.active = false; + document.removeEventListener('mousemove', this.onMouseMove, true); + this.root?.remove(); + this.root = null; + this.xLine = null; + this.yLine = null; + } + + dispose(): void { + document.removeEventListener('keydown', this.onKeyDown); + this.hide(); + } +} diff --git a/packages/playground/src/components/DragDrop.ts b/packages/playground/src/components/DragDrop.ts new file mode 100644 index 000000000..65cc281e5 --- /dev/null +++ b/packages/playground/src/components/DragDrop.ts @@ -0,0 +1,64 @@ +import type * as alphaTab from '@coderline/alphatab'; + +export interface DragDropOptions { + onEnter?: () => void; + onLeave?: () => void; +} + +/** + * Behaviour-only component: attaches document-level drag/drop handlers that + * load a dropped file into the alphaTab API. Does not render any DOM of its own. + */ +export class DragDrop { + private over = (e: DragEvent) => { + e.stopPropagation(); + e.preventDefault(); + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'copy'; + } + if (!this.dragging) { + this.dragging = true; + this.options.onEnter?.(); + } + }; + private leave = (e: DragEvent) => { + // The dragleave fires on every child element transition. Reset only when leaving the document. + if (e.relatedTarget === null) { + this.dragging = false; + this.options.onLeave?.(); + } + }; + private drop = (e: DragEvent) => { + e.stopPropagation(); + e.preventDefault(); + this.dragging = false; + this.options.onLeave?.(); + const files = e.dataTransfer?.files; + if (files && files.length === 1) { + const reader = new FileReader(); + reader.onload = data => { + if (data.target?.result) { + this.api.load(data.target.result, [0]); + } + }; + reader.readAsArrayBuffer(files[0]); + } + }; + + private dragging = false; + + constructor( + private api: alphaTab.AlphaTabApi, + private options: DragDropOptions = {} + ) { + document.addEventListener('dragover', this.over); + document.addEventListener('dragleave', this.leave); + document.addEventListener('drop', this.drop); + } + + dispose(): void { + document.removeEventListener('dragover', this.over); + document.removeEventListener('dragleave', this.leave); + document.removeEventListener('drop', this.drop); + } +} diff --git a/packages/playground/src/components/Footer.ts b/packages/playground/src/components/Footer.ts new file mode 100644 index 000000000..cfe52e242 --- /dev/null +++ b/packages/playground/src/components/Footer.ts @@ -0,0 +1,53 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { TimeSlider } from './TimeSlider'; +import { TransportBar } from './TransportBar'; +import type { TrackList } from './TrackList'; +import { Waveform } from './Waveform'; + +injectStyles( + 'Footer', + css` + .at-footer { + flex: 0 0 auto; + background: var(--at-footer-bg); + color: var(--at-footer-fg); + } + .at-footer a { color: inherit; text-decoration: none; } +` +); + +export interface FooterOptions { + trackList?: TrackList; +} + +export class Footer implements Mountable { + readonly root: HTMLElement; + readonly waveform: Waveform; + readonly timeSlider: TimeSlider; + readonly transport: TransportBar; + + constructor(api: alphaTab.AlphaTabApi, options: FooterOptions = {}) { + this.root = parseHtml(html` + + `); + this.waveform = mount(this.root, '.cmp-waveform', new Waveform(api)); + this.timeSlider = mount(this.root, '.cmp-time-slider', new TimeSlider(api)); + this.transport = mount( + this.root, + '.cmp-transport', + new TransportBar(api, { trackList: options.trackList }) + ); + } + + dispose(): void { + this.waveform.dispose(); + this.timeSlider.dispose(); + this.transport.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/LoadingOverlay.ts b/packages/playground/src/components/LoadingOverlay.ts new file mode 100644 index 000000000..d5e743639 --- /dev/null +++ b/packages/playground/src/components/LoadingOverlay.ts @@ -0,0 +1,86 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Spinner } from './primitives/Spinner'; + +injectStyles( + 'LoadingOverlay', + css` + .at-overlay { + display: flex; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1002; + backdrop-filter: blur(3px); + background: var(--at-overlay-bg); + justify-content: center; + align-items: flex-start; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; + transition-delay: 0; + } + .at-overlay.visible { + visibility: visible; + opacity: 1; + transition-delay: 0.2s; + } + .at-overlay > .at-overlay-content { + margin-top: 20px; + background: #fff; + box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3); + padding: 10px; + } +` +); + +export class LoadingOverlay implements Mountable { + readonly root: HTMLElement; + private unsubRenderStarted: () => void; + private unsubRenderFinished: () => void; + private dragCount = 0; + + constructor(api: alphaTab.AlphaTabApi) { + this.root = parseHtml(html` +
+
+
+
+
+ `); + mount(this.root, '.cmp-spinner', new Spinner()); + + this.unsubRenderStarted = api.renderStarted.on(isResize => { + if (!isResize) { + this.setVisible(true); + } + }); + this.unsubRenderFinished = api.renderFinished.on(() => { + this.setVisible(false); + }); + } + + setVisible(visible: boolean): void { + this.root.classList.toggle('visible', visible); + } + + enterDrag(): void { + this.dragCount++; + this.setVisible(true); + } + + leaveDrag(): void { + this.dragCount = Math.max(0, this.dragCount - 1); + if (this.dragCount === 0) { + this.setVisible(false); + } + } + + dispose(): void { + this.unsubRenderStarted(); + this.unsubRenderFinished(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/NavMenu.ts b/packages/playground/src/components/NavMenu.ts new file mode 100644 index 000000000..375109ac7 --- /dev/null +++ b/packages/playground/src/components/NavMenu.ts @@ -0,0 +1,171 @@ +import { createPopper, type Instance as PopperInstance } from '@popperjs/core'; +import { type DemoEntry, DEMOS } from '../util/Demos'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; +import { Icons, icon as renderIcon } from '../util/Icons'; + +injectStyles( + 'NavMenu', + css` + .at-nav-menu { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 10000; + } + .at-nav-menu-trigger { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + background: rgba(33, 37, 41, 0.92); + color: #fff; + border: 0; + border-radius: 6px; + cursor: pointer; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18); + transition: background-color 0.15s ease-in-out; + } + .at-nav-menu-trigger:hover { background: rgba(33, 37, 41, 1); } + .at-nav-menu-trigger > svg { width: 18px; height: 18px; } + + .at-nav-menu-popover { + position: fixed; + top: 0; + left: 0; + z-index: 20000; + min-width: 220px; + background: #fff; + color: #212529; + border-radius: 6px; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); + padding: 6px 0; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease-in-out; + } + .at-nav-menu-popover.open { opacity: 1; visibility: visible; } + .at-nav-menu-item { + display: block; + padding: 8px 14px; + color: inherit; + text-decoration: none; + font-size: 0.9rem; + } + .at-nav-menu-item:hover { background: rgba(0, 0, 0, 0.06); } + .at-nav-menu-item.active { background: rgba(73, 114, 161, 0.12); font-weight: 500; } + .at-nav-menu-divider { + height: 1px; + background: rgba(0, 0, 0, 0.08); + margin: 4px 0; + } +` +); + +export interface NavMenuOptions { + /** Demos to list. Defaults to the shared `DEMOS` array. */ + items?: DemoEntry[]; + /** Whether to include a "Labs index" link to /. Default: true. */ + includeIndex?: boolean; +} + +export class NavMenu implements Mountable { + readonly root: HTMLElement; + private trigger: HTMLButtonElement; + private popover: HTMLElement; + private popper: PopperInstance | null = null; + private isOpen = false; + + private outsideClick = (e: MouseEvent) => { + if (!this.root.contains(e.target as Node) && !this.popover.contains(e.target as Node)) { + this.close(); + } + }; + private onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape' && this.isOpen) { + e.preventDefault(); + this.close(); + this.trigger.focus(); + } + }; + + constructor(options: NavMenuOptions = {}) { + this.root = parseHtml(html` +
+ +
+ `); + this.trigger = this.root.querySelector('.at-nav-menu-trigger')!; + this.trigger.appendChild(renderIcon(Icons.Menu)); + + this.popover = parseHtml(html``); + + const items = options.items ?? DEMOS; + const includeIndex = options.includeIndex ?? true; + const here = window.location.pathname; + + if (includeIndex) { + const indexLink = parseHtml( + html`Labs index` + ); + if (here === '/' || here === '/index.html') { + indexLink.classList.add('active'); + } + this.popover.appendChild(indexLink); + this.popover.appendChild(parseHtml(html`
`)); + } + + for (const item of items) { + const link = parseHtml( + html`${item.title}` + ); + if (here === item.href || here === `${item.href}index.html`) { + link.classList.add('active'); + } + this.popover.appendChild(link); + } + + this.trigger.addEventListener('click', e => { + e.stopPropagation(); + if (this.isOpen) { + this.close(); + } else { + this.open(); + } + }); + } + + private open(): void { + if (!this.popover.parentElement) { + document.body.appendChild(this.popover); + } + if (!this.popper) { + this.popper = createPopper(this.trigger, this.popover, { + placement: 'bottom-end', + modifiers: [{ name: 'offset', options: { offset: [0, 6] } }] + }); + } else { + this.popper.update(); + } + this.isOpen = true; + this.popover.classList.add('open'); + this.trigger.setAttribute('aria-expanded', 'true'); + document.addEventListener('mousedown', this.outsideClick, true); + document.addEventListener('keydown', this.onKey, true); + } + + private close(): void { + this.isOpen = false; + this.popover.classList.remove('open'); + this.trigger.setAttribute('aria-expanded', 'false'); + document.removeEventListener('mousedown', this.outsideClick, true); + document.removeEventListener('keydown', this.onKey, true); + } + + dispose(): void { + this.close(); + this.popper?.destroy(); + this.popover.remove(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/SelectionHandles.ts b/packages/playground/src/components/SelectionHandles.ts new file mode 100644 index 000000000..e471f779d --- /dev/null +++ b/packages/playground/src/components/SelectionHandles.ts @@ -0,0 +1,155 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; + +injectStyles( + 'SelectionHandles', + css` + .at-selection-handles { + position: absolute; + pointer-events: none; + z-index: 1001; + display: inline; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + .at-selection-handle { + position: absolute; + pointer-events: auto; + cursor: ew-resize; + background: #7cb9ff; + width: 4px; + opacity: 0; + transition: opacity 150ms ease-in-out; + display: none; + } + .at-selection-handle:hover, + .at-selection-handle.dragging { + opacity: 1; + } + .at-selection-handle.active { display: block; } + .at-selection-handle-drag * { cursor: ew-resize !important; } +` +); + +type DragSide = 'start' | 'end'; + +export class SelectionHandles implements Mountable { + readonly root: HTMLElement; + private startHandle: HTMLElement; + private endHandle: HTMLElement; + private currentHighlight: alphaTab.PlaybackHighlightChangeEventArgs | undefined; + private dragging: DragSide | undefined; + private unsubHighlight: () => void; + + private onMouseMove = (e: MouseEvent) => { + if (!this.dragging) { + return; + } + e.preventDefault(); + const beat = this.beatFromEvent(e); + if (!beat || !this.currentHighlight) { + return; + } + if (this.dragging === 'start') { + this.api.highlightPlaybackRange(beat, this.currentHighlight.endBeat!); + } else { + this.api.highlightPlaybackRange(this.currentHighlight.startBeat!, beat); + } + }; + + private onMouseUp = (e: MouseEvent) => { + if (!this.dragging) { + return; + } + e.preventDefault(); + this.endDrag(); + this.api.applyPlaybackRangeFromHighlight(); + }; + + constructor( + private api: alphaTab.AlphaTabApi, + private viewportEl: HTMLElement + ) { + this.root = parseHtml(html` +
+
+
+
+ `); + this.startHandle = this.root.querySelector('.at-selection-handle-start')!; + this.endHandle = this.root.querySelector('.at-selection-handle-end')!; + + this.startHandle.addEventListener('mousedown', e => this.beginDrag(e, 'start')); + this.endHandle.addEventListener('mousedown', e => this.beginDrag(e, 'end')); + document.addEventListener('mousemove', this.onMouseMove, true); + document.addEventListener('mouseup', this.onMouseUp, true); + + this.unsubHighlight = api.playbackRangeHighlightChanged.on(e => this.update(e)); + } + + private beginDrag(e: MouseEvent, side: DragSide): void { + e.preventDefault(); + this.dragging = side; + this.viewportEl.classList.add('at-selection-handle-drag'); + const handle = side === 'start' ? this.startHandle : this.endHandle; + handle.classList.add('dragging'); + } + + private endDrag(): void { + if (!this.dragging) { + return; + } + const handle = this.dragging === 'start' ? this.startHandle : this.endHandle; + handle.classList.remove('dragging'); + this.viewportEl.classList.remove('at-selection-handle-drag'); + this.dragging = undefined; + } + + private update(e: alphaTab.PlaybackHighlightChangeEventArgs): void { + this.currentHighlight = e; + if (!e.startBeat || !e.endBeat) { + this.startHandle.classList.remove('active'); + this.endHandle.classList.remove('active'); + return; + } + const startBounds = e.startBeatBounds!; + const endBounds = e.endBeatBounds!; + this.startHandle.classList.add('active'); + this.startHandle.style.left = `${startBounds.realBounds.x}px`; + this.startHandle.style.top = `${startBounds.barBounds.masterBarBounds.visualBounds.y}px`; + this.startHandle.style.height = `${startBounds.barBounds.masterBarBounds.visualBounds.h}px`; + this.endHandle.classList.add('active'); + this.endHandle.style.left = `${endBounds.realBounds.x + endBounds.realBounds.w}px`; + this.endHandle.style.top = `${endBounds.barBounds.masterBarBounds.visualBounds.y}px`; + this.endHandle.style.height = `${endBounds.barBounds.masterBarBounds.visualBounds.h}px`; + } + + private beatFromEvent(e: MouseEvent): alphaTab.model.Beat | undefined { + const rect = this.viewportEl.getBoundingClientRect(); + const relX = e.clientX - rect.left; + const relY = e.clientY - rect.top; + const beat = this.api.boundsLookup?.getBeatAtPos(relX, relY); + if (!beat) { + return undefined; + } + const bounds = this.api.boundsLookup!.findBeat(beat); + if (!bounds) { + return undefined; + } + const visualEnd = bounds.visualBounds.x + bounds.visualBounds.w; + const realEnd = bounds.realBounds.x + bounds.realBounds.w; + if (relX < visualEnd || relX > realEnd) { + return undefined; + } + return beat; + } + + dispose(): void { + document.removeEventListener('mousemove', this.onMouseMove, true); + document.removeEventListener('mouseup', this.onMouseUp, true); + this.unsubHighlight(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/Sidebar.ts b/packages/playground/src/components/Sidebar.ts new file mode 100644 index 000000000..84bc420b0 --- /dev/null +++ b/packages/playground/src/components/Sidebar.ts @@ -0,0 +1,56 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { TrackList } from './TrackList'; + +injectStyles( + 'Sidebar', + css` + .at-sidebar { + max-width: 70px; + width: auto; + display: flex; + align-content: stretch; + z-index: 1001; + position: absolute; + top: 0; + left: 0; + bottom: 0; + overflow: hidden; + border-right: 1px solid rgba(0, 0, 0, 0.12); + background: var(--at-sidebar-bg); + } + .at-sidebar:hover { + max-width: 400px; + transition: max-width 0.2s; + overflow-y: auto; + } + .at-sidebar > .at-sidebar-content { + flex: 1 1 auto; + min-width: 0; + } + @media screen and (max-width: 1100px) { + .at-sidebar { display: none; } + } +` +); + +export class Sidebar implements Mountable { + readonly root: HTMLElement; + readonly trackList: TrackList; + + constructor(api: alphaTab.AlphaTabApi) { + this.root = parseHtml(html` +
+
+
+
+
+ `); + this.trackList = mount(this.root, '.cmp-track-list', new TrackList(api)); + } + + dispose(): void { + this.trackList.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/TimeSlider.ts b/packages/playground/src/components/TimeSlider.ts new file mode 100644 index 000000000..5784ed2b9 --- /dev/null +++ b/packages/playground/src/components/TimeSlider.ts @@ -0,0 +1,48 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { ProgressBar } from './primitives/ProgressBar'; + +injectStyles( + 'TimeSlider', + css` + .at-time-slider-wrap { width: 100%; } +` +); + +export class TimeSlider implements Mountable { + readonly root: HTMLElement; + private bar: ProgressBar; + private endTime = 0; + private unsubMidiLoaded: () => void; + private unsubPosition: () => void; + + constructor(api: alphaTab.AlphaTabApi) { + this.root = parseHtml(html` +
+
+
+ `); + this.bar = mount(this.root, '.cmp-progress', new ProgressBar()); + this.bar.onClickPercent = percent => { + if (this.endTime > 0) { + api.timePosition = Math.floor(this.endTime * percent); + } + }; + + this.unsubMidiLoaded = api.midiLoaded.on(e => { + this.endTime = e.endTime; + }); + this.unsubPosition = api.playerPositionChanged.on(args => { + if (args.endTime > 0) { + this.bar.setValue(args.currentTime / args.endTime); + } + }); + } + + dispose(): void { + this.unsubMidiLoaded(); + this.unsubPosition(); + this.bar.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/TrackItem.ts b/packages/playground/src/components/TrackItem.ts new file mode 100644 index 000000000..b7b50e772 --- /dev/null +++ b/packages/playground/src/components/TrackItem.ts @@ -0,0 +1,180 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { type IconNode, Icons, icon as renderIcon } from '../util/Icons'; +import { Slider } from './primitives/Slider'; +import { ToggleButton } from './primitives/ToggleButton'; + +function pickTrackIcon(track: alphaTab.model.Track): IconNode { + if (track.playbackInfo.primaryChannel === 9 || track.staves[0]?.isPercussion) { + return Icons.TrackDrum; + } + const program = track.playbackInfo.program; + if (program >= 0 && program <= 7) { + // Acoustic Grand .. Clavi + return Icons.TrackPiano; + } + if ((program >= 52 && program <= 54) || program === 85) { + // Choir Aahs / Voice Oohs / Synth Voice / Lead 6 (voice) + return Icons.TrackVoice; + } + if (program >= 112 && program <= 119) { + // Percussive program range (Tinkle Bell .. Reverse Cymbal) + return Icons.TrackDrum; + } + return Icons.Track; +} + +injectStyles( + 'TrackItem', + css` + .at-track { + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: auto auto; + grid-template-areas: 'icon title' 'icon controls'; + padding: 5px; + transition: background 0.2s; + grid-gap: 5px; + cursor: pointer; + } + .at-track:hover { background: rgba(0, 0, 0, 0.1); } + .at-track.active { background: var(--at-track-active-bg); } + .at-track > .at-track-icon { + grid-area: icon; + font-size: 32px; + display: flex; + justify-content: center; + align-items: center; + opacity: 0.5; + transition: opacity 0.2s; + width: 64px; + height: 64px; + color: inherit; + } + .at-track > .at-track-icon > svg { width: 32px; height: 32px; } + .at-track:hover > .at-track-icon { opacity: 0.8; } + .at-track.active > .at-track-icon { color: var(--at-accent); opacity: 1; } + .at-track > .at-track-name { + grid-area: title; + font-weight: 500; + } + .at-track > .at-track-controls { + grid-area: controls; + display: flex; + align-items: center; + } + .at-track > .at-track-controls > * { margin: 0 2px; } + .at-track > .at-track-controls > .at-track-mute, + .at-track > .at-track-controls > .at-track-solo { + display: inline-flex; + align-items: center; + height: 26px; + padding: 2px 8px; + font-size: 11px; + border: 1px solid; + background: transparent; + cursor: pointer; + border-radius: 4px; + } + .at-track-mute { color: #dc3545; border-color: #dc3545; } + .at-track-mute.at-toggle-active { background: #dc3545; color: #fff; } + .at-track-solo { color: #198754; border-color: #198754; } + .at-track-solo.at-toggle-active { background: #198754; color: #fff; } + .at-track > .at-track-controls > .at-track-volume-icon { display: inline-flex; } + .at-track > .at-track-controls > .at-track-volume-icon > svg { width: 14px; height: 14px; } +` +); + +export class TrackItem implements Mountable { + readonly root: HTMLElement; + readonly track: alphaTab.model.Track; + + private muteBtn: ToggleButton; + private soloBtn: ToggleButton; + private volume: Slider; + + onSelect: ((e: MouseEvent) => void) | null = null; + + constructor(api: alphaTab.AlphaTabApi, track: alphaTab.model.Track) { + this.track = track; + this.root = parseHtml(html` +
+
+ ${track.name} +
+
+
+ +
+
+
+ `); + this.root.querySelector('.at-track-icon')!.appendChild(renderIcon(pickTrackIcon(track))); + this.root.querySelector('.at-track-volume-icon')!.appendChild(renderIcon(Icons.Volume)); + + this.muteBtn = mount( + this.root, + '.cmp-mute', + new ToggleButton({ icon: Icons.Track, label: 'Mute', tooltip: 'Mute track' }) + ); + // Replace the IconButton svg with simple text button by clearing the icon slot + const muteIconSlot = this.muteBtn.root.querySelector('.at-icon-btn-icon'); + if (muteIconSlot) { + muteIconSlot.remove(); + } + this.muteBtn.root.classList.remove('at-icon-btn'); + this.muteBtn.root.classList.add('at-track-mute'); + this.muteBtn.onChange = active => { + api.changeTrackMute([this.track], active); + }; + + this.soloBtn = mount( + this.root, + '.cmp-solo', + new ToggleButton({ icon: Icons.Track, label: 'Solo', tooltip: 'Solo track' }) + ); + const soloIconSlot = this.soloBtn.root.querySelector('.at-icon-btn-icon'); + if (soloIconSlot) { + soloIconSlot.remove(); + } + this.soloBtn.root.classList.remove('at-icon-btn'); + this.soloBtn.root.classList.add('at-track-solo'); + this.soloBtn.onChange = active => { + api.changeTrackSolo([this.track], active); + }; + + this.volume = mount( + this.root, + '.cmp-volume', + new Slider({ min: 0, max: 16, step: 1, initialValue: track.playbackInfo.volume }) + ); + this.volume.onInput = value => { + api.changeTrackVolume([this.track], value / Math.max(1, this.track.playbackInfo.volume)); + }; + + this.root.addEventListener('click', e => this.onSelect?.(e)); + } + + setActive(active: boolean): void { + this.root.classList.toggle('active', active); + } + + isMuted(): boolean { + return this.muteBtn.isActive(); + } + + isSoloed(): boolean { + return this.soloBtn.isActive(); + } + + getVolume(): number { + return this.volume.getValue(); + } + + dispose(): void { + this.muteBtn.dispose(); + this.soloBtn.dispose(); + this.volume.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/TrackList.ts b/packages/playground/src/components/TrackList.ts new file mode 100644 index 000000000..3b6ba4be0 --- /dev/null +++ b/packages/playground/src/components/TrackList.ts @@ -0,0 +1,81 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; +import { TrackItem } from './TrackItem'; + +injectStyles( + 'TrackList', + css` + .at-track-list { + display: flex; + flex-direction: column; + } +` +); + +export class TrackList implements Mountable { + readonly root: HTMLElement; + private items: TrackItem[] = []; + private selection = new Map(); + private unsubScoreLoaded: () => void; + private unsubRenderStarted: () => void; + + constructor(private api: alphaTab.AlphaTabApi) { + this.root = parseHtml(html`
`); + + this.unsubScoreLoaded = api.scoreLoaded.on(score => this.rebuild(score)); + this.unsubRenderStarted = api.renderStarted.on(() => this.refreshActive()); + } + + private rebuild(score: alphaTab.model.Score): void { + for (const item of this.items) { + item.dispose(); + } + this.items = []; + this.root.replaceChildren(); + this.selection.clear(); + + for (const track of score.tracks) { + const item = new TrackItem(this.api, track); + item.onSelect = e => { + e.stopPropagation(); + if (!e.ctrlKey) { + this.selection.clear(); + this.selection.set(track.index, track); + } else if (this.selection.has(track.index)) { + this.selection.delete(track.index); + } else { + this.selection.set(track.index, track); + } + this.api.renderTracks(Array.from(this.selection.values()).sort((a, b) => a.index - b.index)); + }; + this.items.push(item); + this.root.appendChild(item.root); + } + this.refreshActive(); + } + + private refreshActive(): void { + const active = new Set(); + for (const t of this.api.tracks) { + active.add(t.index); + } + for (const item of this.items) { + item.setActive(active.has(item.track.index)); + } + } + + /** All TrackItem instances, in score order. */ + getItems(): readonly TrackItem[] { + return this.items; + } + + dispose(): void { + this.unsubScoreLoaded(); + this.unsubRenderStarted(); + for (const item of this.items) { + item.dispose(); + } + this.items = []; + this.root.remove(); + } +} diff --git a/packages/playground/src/components/TransportBar.ts b/packages/playground/src/components/TransportBar.ts new file mode 100644 index 000000000..c9d365119 --- /dev/null +++ b/packages/playground/src/components/TransportBar.ts @@ -0,0 +1,394 @@ +import * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, mount, parseHtml } from '../util/Dom'; +import { Icons } from '../util/Icons'; +import { exportAudio, exportGp7 } from './AudioExporter'; +import { Dropdown, type DropdownItem } from './primitives/Dropdown'; +import { IconButton } from './primitives/IconButton'; +import { LoadingProgress } from './primitives/LoadingProgress'; +import { ToggleButton } from './primitives/ToggleButton'; +import type { TrackList } from './TrackList'; + +injectStyles( + 'TransportBar', + css` + .at-transport { + display: flex; + justify-content: space-between; + align-items: center; + background: var(--at-footer-bg); + color: var(--at-footer-fg); + } + .at-transport-left, + .at-transport-right { + display: flex; + align-items: center; + padding: 3px; + } + .at-transport-left > *, + .at-transport-right > * { + margin-right: 4px; + } + .at-transport-separator { + align-self: stretch; + width: 1px; + margin: 6px 4px; + background: var(--at-divider); + } + .at-song-details { + font-size: 12px; + padding: 0 8px; + } + .at-song-details > .at-song-title { font-weight: 500; } + .at-time-position { + font-weight: bold; + padding: 0 8px; + } + .at-loading-slot { display: inline-flex; align-items: center; } + .at-loading-slot.hidden { display: none; } + + @media screen and (max-width: 920px) { + .at-transport-right > *:not(.at-transport-essential) { display: none !important; } + } + @media screen and (max-width: 1100px) { + .at-transport * { font-size: 12px !important; } + } +` +); + +const SPEED_ITEMS: DropdownItem[] = [ + { value: 0.25, label: '0.25x' }, + { value: 0.5, label: '0.5x' }, + { value: 0.75, label: '0.75x' }, + { value: 0.9, label: '0.9x' }, + { value: 1, label: '1x' }, + { value: 1.1, label: '1.1x' }, + { value: 1.25, label: '1.25x' }, + { value: 1.5, label: '1.5x' }, + { value: 2, label: '2x' } +]; + +const ZOOM_ITEMS: DropdownItem[] = [ + { value: 0.25, label: '25%' }, + { value: 0.5, label: '50%' }, + { value: 0.75, label: '75%' }, + { value: 0.9, label: '90%' }, + { value: 1, label: '100%' }, + { value: 1.1, label: '110%' }, + { value: 1.25, label: '125%' }, + { value: 1.5, label: '150%' }, + { value: 2, label: '200%' } +]; + +const LAYOUT_ITEMS: DropdownItem[] = [ + { value: alphaTab.LayoutMode.Horizontal, label: 'Horizontal', icon: Icons.LayoutHorizontal }, + { value: alphaTab.LayoutMode.Page, label: 'Vertical', icon: Icons.LayoutPage }, + { value: alphaTab.LayoutMode.Parchment, label: 'Parchment', icon: Icons.LayoutParchment } +]; + +const SCROLL_ITEMS: DropdownItem[] = [ + { value: alphaTab.ScrollMode.Off, label: 'No automatic Scrolling', icon: Icons.ScrollOff }, + { value: alphaTab.ScrollMode.Continuous, label: 'On bar change (Continuous)', icon: Icons.ScrollContinuous }, + { value: alphaTab.ScrollMode.OffScreen, label: 'On bar change (Out of Screen)', icon: Icons.ScrollOffScreen }, + { value: alphaTab.ScrollMode.Smooth, label: 'Smooth', icon: Icons.ScrollSmooth } +]; + +export interface TransportBarOptions { + trackList?: TrackList; +} + +export class TransportBar implements Mountable { + readonly root: HTMLElement; + private playPause: IconButton; + private stop: IconButton; + private metronome: ToggleButton; + private countIn: ToggleButton; + private loop: ToggleButton; + private outputDevice: Dropdown; + private loadingProgress: LoadingProgress; + private loadingSlot: HTMLElement; + private titleEl: HTMLElement; + private artistEl: HTMLElement; + private timePositionEl: HTMLElement; + private subscriptions: (() => void)[] = []; + private outputDevices: alphaTab.synth.ISynthOutputDevice[] = []; + private previousTime = -1; + + constructor( + api: alphaTab.AlphaTabApi, + private options: TransportBarOptions = {} + ) { + this.root = parseHtml(html` +
+
+
+
+
+ +
+ - + +
+
00:00 / 00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `); + this.titleEl = this.root.querySelector('.at-song-title')!; + this.artistEl = this.root.querySelector('.at-song-artist')!; + this.timePositionEl = this.root.querySelector('.at-time-position')!; + this.loadingSlot = this.root.querySelector('.at-loading-slot')!; + + // --- left group --- + this.stop = mount( + this.root, + '.cmp-stop', + new IconButton({ icon: Icons.Stop, tooltip: 'Stop' }) + ); + this.stop.setEnabled(false); + this.stop.onClick = () => api.stop(); + + this.playPause = mount( + this.root, + '.cmp-play-pause', + new IconButton({ icon: Icons.Play, tooltip: 'Play/Pause' }) + ); + this.playPause.setEnabled(false); + this.playPause.onClick = () => api.playPause(); + + const speed = mount( + this.root, + '.cmp-speed', + new Dropdown({ + icon: Icons.Search, + label: '1x', + tooltip: 'Playback speed', + items: SPEED_ITEMS, + initialValue: 1 + }) + ); + speed.onSelect = (value, item) => { + api.playbackSpeed = value; + speed.setLabel(item.label); + }; + + this.loadingProgress = mount(this.loadingSlot, '.cmp-loading', new LoadingProgress()); + + // --- right group --- + this.outputDevice = mount( + this.root, + '.cmp-output-device', + new Dropdown({ + icon: Icons.OutputDevice, + tooltip: 'Output device', + items: [{ value: '', label: 'Default' }], + onOpen: async () => { + const devices = await api.enumerateOutputDevices(); + this.outputDevices = devices; + return [ + { value: '', label: 'Default' }, + ...devices.map(d => ({ value: d.deviceId, label: d.label + (d.isDefault ? ' (default)' : '') })) + ]; + } + }) + ); + this.outputDevice.onSelect = async value => { + if (!value) { + await api.setOutputDevice(null); + return; + } + const device = this.outputDevices.find(d => d.deviceId === value); + if (device) { + await api.setOutputDevice(device); + } + }; + + this.countIn = mount( + this.root, + '.cmp-count-in', + new ToggleButton({ icon: Icons.CountIn, tooltip: 'Count-In' }) + ); + this.countIn.setEnabled(false); + this.countIn.onChange = on => { + api.countInVolume = on ? 1 : 0; + }; + + this.metronome = mount( + this.root, + '.cmp-metronome', + new ToggleButton({ icon: Icons.Metronome, tooltip: 'Metronome' }) + ); + this.metronome.setEnabled(false); + this.metronome.onChange = on => { + api.metronomeVolume = on ? 1 : 0; + }; + + this.loop = mount( + this.root, + '.cmp-loop', + new ToggleButton({ icon: Icons.Loop, tooltip: 'Loop' }) + ); + this.loop.setEnabled(false); + this.loop.onChange = on => { + api.isLooping = on; + }; + + const print = mount( + this.root, + '.cmp-print', + new IconButton({ icon: Icons.Print, tooltip: 'Print' }) + ); + print.onClick = () => api.print(); + + const downloadGp = mount( + this.root, + '.cmp-download-gp', + new IconButton({ icon: Icons.DownloadGp, tooltip: 'Download GP' }) + ); + downloadGp.onClick = () => exportGp7(api); + + const downloadAudio = mount( + this.root, + '.cmp-download-audio', + new IconButton({ icon: Icons.DownloadAudio, tooltip: 'Download Audio Data' }) + ); + downloadAudio.onClick = async () => { + await exportAudio(api, this.options.trackList?.getItems() ?? []); + }; + + const zoom = mount( + this.root, + '.cmp-zoom', + new Dropdown({ + icon: Icons.Search, + label: '100%', + tooltip: 'Zoom', + items: ZOOM_ITEMS, + initialValue: 1 + }) + ); + zoom.onSelect = (value, item) => { + api.settings.display.scale = value; + zoom.setLabel(item.label); + api.updateSettings(); + api.render(); + }; + + const layout = mount( + this.root, + '.cmp-layout', + new Dropdown({ + label: 'Layout', + tooltip: 'Layout mode', + items: LAYOUT_ITEMS, + initialValue: api.settings.display.layoutMode + }) + ); + layout.onSelect = value => { + api.settings.display.layoutMode = value; + api.updateSettings(); + api.render(); + }; + + const scroll = mount( + this.root, + '.cmp-scroll', + new Dropdown({ + label: 'Scroll', + tooltip: 'Scroll mode', + items: SCROLL_ITEMS, + initialValue: api.settings.player.scrollMode + }) + ); + scroll.onSelect = value => { + api.settings.player.scrollMode = value; + switch (value) { + case alphaTab.ScrollMode.Continuous: + case alphaTab.ScrollMode.OffScreen: + api.settings.player.scrollOffsetX = -10; + api.settings.player.scrollOffsetY = -10; + break; + case alphaTab.ScrollMode.Smooth: + api.settings.player.scrollOffsetX = -50; + api.settings.player.scrollOffsetY = -100; + break; + } + api.updateSettings(); + api.render(); + }; + + // --- subscriptions --- + this.subscriptions.push( + api.scoreLoaded.on(score => { + this.titleEl.textContent = score.title; + this.artistEl.textContent = score.artist; + }) + ); + this.subscriptions.push( + api.playerStateChanged.on(args => { + this.playPause.setIcon( + args.state === alphaTab.synth.PlayerState.Playing ? Icons.Pause : Icons.Play + ); + }) + ); + this.subscriptions.push( + api.playerPositionChanged.on(args => { + const sec = (args.currentTime / 1000) | 0; + if (sec === this.previousTime) { + return; + } + this.previousTime = sec; + this.timePositionEl.textContent = `${formatDuration(args.currentTime)} / ${formatDuration(args.endTime)}`; + }) + ); + this.subscriptions.push( + api.soundFontLoad.on(args => { + this.loadingSlot.classList.remove('hidden'); + this.loadingProgress.setValue(args.loaded / Math.max(1, args.total)); + }) + ); + this.subscriptions.push( + api.soundFontLoaded.on(() => { + this.loadingSlot.classList.add('hidden'); + }) + ); + this.subscriptions.push( + api.playerReady.on(() => { + this.playPause.setEnabled(true); + this.stop.setEnabled(true); + this.metronome.setEnabled(true); + this.countIn.setEnabled(true); + this.loop.setEnabled(true); + }) + ); + } + + dispose(): void { + for (const u of this.subscriptions) { + u(); + } + this.subscriptions = []; + this.root.remove(); + } +} + +function formatDuration(milliseconds: number): string { + let seconds = milliseconds / 1000; + const minutes = (seconds / 60) | 0; + seconds = (seconds - minutes * 60) | 0; + return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; +} diff --git a/packages/playground/src/components/Waveform.ts b/packages/playground/src/components/Waveform.ts new file mode 100644 index 000000000..2e1c8ce27 --- /dev/null +++ b/packages/playground/src/components/Waveform.ts @@ -0,0 +1,184 @@ +import * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../util/Dom'; + +injectStyles( + 'Waveform', + css` + .at-waveform { + position: relative; + background: #f7f7f7; + border-top: 1px solid rgba(0, 0, 0, 0.12); + cursor: pointer; + } + .at-waveform.hidden { display: none; } + .at-waveform > canvas { + width: 100%; + opacity: 0.5; + display: block; + } + .at-waveform > .at-waveform-cursor { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 1px; + background: var(--at-cursor-beat); + } +` +); + +export class Waveform implements Mountable { + readonly root: HTMLElement; + private cursor: HTMLElement; + private canvas: HTMLCanvasElement | null = null; + private currentScore: alphaTab.model.Score | null = null; + private audioElement: HTMLAudioElement | null = null; + private updateCursor = () => this.refreshCursor(); + private unsubScoreLoaded: () => void; + private unsubPlayerReady: () => void; + private unsubStateChanged: () => void; + + constructor(private api: alphaTab.AlphaTabApi) { + this.root = parseHtml(html` + + `); + this.cursor = this.root.querySelector('.at-waveform-cursor')!; + this.root.addEventListener('click', e => { + if (this.audioElement) { + const rect = this.root.getBoundingClientRect(); + const percent = (e.clientX - rect.left) / rect.width; + this.audioElement.currentTime = this.audioElement.duration * percent; + } + }); + + this.unsubScoreLoaded = api.scoreLoaded.on(() => this.refresh()); + this.unsubPlayerReady = api.playerReady.on(() => this.refresh()); + this.unsubStateChanged = api.playerStateChanged.on(() => this.refresh()); + } + + private async refresh(): Promise { + const score = this.api.score; + const player = this.api.player; + if (!score || !player || !score.backingTrack || this.api.actualPlayerMode !== alphaTab.PlayerMode.EnabledBackingTrack) { + this.hide(); + return; + } + + const output = player.output as alphaTab.synth.IAudioElementBackingTrackSynthOutput; + if (typeof output.audioElement === 'undefined') { + this.hide(); + return; + } + this.bindAudio(output.audioElement); + + if (score === this.currentScore) { + return; + } + this.currentScore = score; + this.root.classList.remove('hidden'); + await this.draw(score.backingTrack); + } + + private bindAudio(audio: HTMLAudioElement): void { + if (this.audioElement === audio) { + return; + } + this.unbindAudio(); + this.audioElement = audio; + audio.addEventListener('timeupdate', this.updateCursor); + audio.addEventListener('durationchange', this.updateCursor); + audio.addEventListener('seeked', this.updateCursor); + } + + private unbindAudio(): void { + if (!this.audioElement) { + return; + } + this.audioElement.removeEventListener('timeupdate', this.updateCursor); + this.audioElement.removeEventListener('durationchange', this.updateCursor); + this.audioElement.removeEventListener('seeked', this.updateCursor); + this.audioElement = null; + } + + private hide(): void { + this.root.classList.add('hidden'); + this.unbindAudio(); + this.currentScore = null; + } + + private refreshCursor(): void { + if (!this.audioElement || !this.audioElement.duration) { + return; + } + this.cursor.style.left = `${(this.audioElement.currentTime / this.audioElement.duration) * 100}%`; + } + + private async draw(backingTrack: alphaTab.model.BackingTrack): Promise { + const buffer = backingTrack.rawAudioFile; + if (!buffer) { + return; + } + + const audioContext = new AudioContext(); + const data = await audioContext.decodeAudioData(structuredClone(buffer.buffer) as ArrayBuffer); + + const top = data.getChannelData(0); + const bottom = data.numberOfChannels > 1 ? data.getChannelData(1) : top; + const length = top.length; + + if (!this.canvas) { + this.canvas = document.createElement('canvas'); + this.root.insertBefore(this.canvas, this.cursor); + } + const width = this.root.offsetWidth || 600; + const height = 80; + this.canvas.width = width; + this.canvas.height = height; + const ctx = this.canvas.getContext('2d'); + if (!ctx) { + return; + } + const pixelRatio = window.devicePixelRatio || 1; + const halfHeight = height / 2; + const barWidth = 2 * pixelRatio; + const barGap = 1 * pixelRatio; + const barIndexScale = width / (barWidth + barGap) / length; + + ctx.beginPath(); + let prevX = 0; + let maxTop = 0; + let maxBottom = 0; + for (let i = 0; i <= length; i++) { + const x = Math.round(i * barIndexScale); + if (x > prevX) { + const topBarHeight = Math.round(maxTop * halfHeight); + const bottomBarHeight = Math.round(maxBottom * halfHeight); + const barHeight = topBarHeight + bottomBarHeight || 1; + ctx.roundRect(prevX * (barWidth + barGap), halfHeight - topBarHeight, barWidth, barHeight, 2); + prevX = x; + maxTop = 0; + maxBottom = 0; + } + const mt = Math.abs(top[i] || 0); + const mb = Math.abs(bottom[i] || 0); + if (mt > maxTop) { + maxTop = mt; + } + if (mb > maxBottom) { + maxBottom = mb; + } + } + ctx.fillStyle = '#436d9d'; + ctx.fill(); + } + + dispose(): void { + this.unsubScoreLoaded(); + this.unsubPlayerReady(); + this.unsubStateChanged(); + this.unbindAudio(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/alphatex-editor/MonacoEditor.ts b/packages/playground/src/components/alphatex-editor/MonacoEditor.ts new file mode 100644 index 000000000..bb75769e2 --- /dev/null +++ b/packages/playground/src/components/alphatex-editor/MonacoEditor.ts @@ -0,0 +1,98 @@ +import * as alphaTab from '@coderline/alphatab'; +import { registerAlphaTexGrammar } from '@coderline/alphatab-monaco/alphatex'; +import { basicEditorLspIntegration } from '@coderline/alphatab-monaco/lsp'; +import { addTextMateGrammarSupport } from '@coderline/alphatab-monaco/textmate'; +import * as monaco from 'monaco-editor'; +// @ts-expect-error worker import handled by Vite +import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'MonacoEditor', + css` + .at-monaco { height: 100%; } +` +); + +let monacoSetupPromise: Promise | null = null; + +async function setupMonaco(): Promise { + if (!monacoSetupPromise) { + monacoSetupPromise = (async () => { + self.MonacoEnvironment = { + getWorker: () => new editorWorker() + }; + const onigurumaWasm = await fetchArrayBuffer( + new URL('vscode-oniguruma/release/onig.wasm', import.meta.url).toString() + ); + const textMateSupport = addTextMateGrammarSupport(onigurumaWasm); + await registerAlphaTexGrammar(textMateSupport); + })(); + } + return monacoSetupPromise; +} + +async function fetchArrayBuffer(url: string): Promise { + const res = await fetch(url); + if (!res.ok) { + throw new Error(`Failed to fetch ${url}: ${res.status}`); + } + return res.arrayBuffer(); +} + +export interface MonacoEditorProps { + initialCode: string; +} + +export class MonacoEditor implements Mountable { + readonly root: HTMLElement; + readonly ready: Promise; + private editor: monaco.editor.IStandaloneCodeEditor | null = null; + + onChange: ((tex: string) => void) | null = null; + + constructor(private props: MonacoEditorProps) { + this.root = parseHtml(html`
`); + this.ready = this.init(); + } + + private async init(): Promise { + await setupMonaco(); + this.editor = monaco.editor.create(this.root, { + value: this.props.initialCode, + language: 'alphatex', + automaticLayout: true + }); + this.editor.onDidChangeModelContent(() => { + this.onChange?.(this.editor!.getModel()!.getValue()); + }); + await basicEditorLspIntegration( + this.editor, + new Worker(new URL('../../../alphatexLanguageServerWrap.ts', import.meta.url), { type: 'module' }), + { + logger: { + error: m => alphaTab.Logger.error('LanguageServer', m), + info: m => alphaTab.Logger.info('LanguageServer', m), + log: m => alphaTab.Logger.debug('LanguageServer', m), + warn: m => alphaTab.Logger.warning('LanguageServer', m) + }, + clientInfo: { name: 'alphaTab Playground', version: 'latest' }, + languageId: 'alphatex' + } + ); + } + + getValue(): string { + return this.editor?.getModel()?.getValue() ?? this.props.initialCode; + } + + setValue(value: string): void { + this.editor?.getModel()?.setValue(value); + } + + dispose(): void { + this.editor?.dispose(); + this.editor = null; + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/Dropdown.ts b/packages/playground/src/components/primitives/Dropdown.ts new file mode 100644 index 000000000..e6d1f58e3 --- /dev/null +++ b/packages/playground/src/components/primitives/Dropdown.ts @@ -0,0 +1,270 @@ +import { createPopper, type Instance as PopperInstance } from '@popperjs/core'; +import { type Mountable, css, escapeHtml, html, injectStyles, parseHtml } from '../../util/Dom'; +import type { IconNode } from '../../util/Icons'; +import { icon as renderIcon } from '../../util/Icons'; +import { Tooltip } from './Tooltip'; + +injectStyles( + 'Dropdown', + css` + .at-dropdown { + position: relative; + display: inline-flex; + } + .at-dropdown-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + height: 40px; + min-width: 40px; + border: 0; + background: transparent; + color: inherit; + cursor: pointer; + font: inherit; + transition: background-color 0.15s ease-in-out; + } + .at-dropdown-toggle:hover:not([disabled]), + .at-dropdown.open > .at-dropdown-toggle { background: var(--at-accent-hover); } + .at-dropdown-toggle[disabled] { opacity: 0.4; cursor: default; } + .at-dropdown-toggle > .at-dropdown-icon { display: inline-flex; } + .at-dropdown-toggle > .at-dropdown-icon > svg { width: 16px; height: 16px; } + .at-dropdown-toggle > .at-dropdown-label { font-size: 12px; } + .at-dropdown-toggle > .at-dropdown-caret > svg { width: 12px; height: 12px; } + + .at-dropdown-menu { + position: fixed; + top: 0; + left: 0; + z-index: 20000; + min-width: 140px; + background: #fff; + color: #212529; + border-radius: 4px; + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), + 0 4px 5px 0 rgba(0, 0, 0, 0.14), + 0 1px 10px 0 rgba(0, 0, 0, 0.12); + padding: 4px 0; + margin: 0; + list-style: none; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease-in-out; + } + .at-dropdown-menu.open { opacity: 1; visibility: visible; } + .at-dropdown-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + cursor: pointer; + white-space: nowrap; + background: transparent; + border: 0; + color: inherit; + font: inherit; + text-align: left; + width: 100%; + } + .at-dropdown-item:hover { background: rgba(0, 0, 0, 0.06); } + .at-dropdown-item.active { background: rgba(0, 0, 0, 0.08); font-weight: 500; } + .at-dropdown-item > .at-dropdown-item-icon > svg { width: 16px; height: 16px; } +` +); + +export interface DropdownItem { + value: T; + label: string; + icon?: IconNode; +} + +export interface DropdownProps { + icon?: IconNode; + label?: string; + tooltip?: string; + items?: DropdownItem[]; + initialValue?: T; + placement?: 'top' | 'bottom'; + /** Called the first time the dropdown opens; can populate items dynamically. */ + onOpen?: () => DropdownItem[] | Promise[]> | undefined; +} + +export class Dropdown implements Mountable { + readonly root: HTMLElement; + private toggle: HTMLButtonElement; + private labelEl: HTMLElement; + private menu: HTMLElement; + private popper: PopperInstance | null = null; + private items: DropdownItem[]; + private currentValue: T | undefined; + private isOpen = false; + private tooltip: Tooltip | null = null; + private outsideClick = (e: MouseEvent) => { + if (!this.root.contains(e.target as Node) && !this.menu.contains(e.target as Node)) { + this.close(); + } + }; + private onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape' && this.isOpen) { + e.preventDefault(); + this.close(); + this.toggle.focus(); + } + }; + + onSelect: ((value: T, item: DropdownItem) => void) | null = null; + + constructor(private props: DropdownProps) { + this.items = props.items ?? []; + this.currentValue = props.initialValue; + + const iconHtml = props.icon ? '' : ''; + const labelHtml = props.label ? `${escapeHtml(props.label)}` : ''; + this.root = parseHtml(html` +
+ +
+ `); + // restore raw HTML overrides since `html` escaped the slot strings above + const toggleEl = this.root.querySelector('.at-dropdown-toggle')!; + toggleEl.innerHTML = `${iconHtml}${labelHtml}`; + this.toggle = toggleEl; + this.labelEl = this.toggle.querySelector('.at-dropdown-label')!; + if (props.icon) { + this.toggle.querySelector('.at-dropdown-icon')!.appendChild(renderIcon(props.icon)); + } + // small caret + const caretSlot = this.toggle.querySelector('.at-dropdown-caret')!; + caretSlot.appendChild(this.makeCaret()); + + this.menu = parseHtml(html``); + this.renderItems(); + + if (props.tooltip) { + this.tooltip = new Tooltip(this.toggle, props.tooltip); + } + + this.toggle.addEventListener('click', e => { + e.stopPropagation(); + if (this.isOpen) { + this.close(); + } else { + this.open(); + } + }); + } + + private makeCaret(): SVGSVGElement { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('viewBox', '0 0 12 12'); + svg.setAttribute('width', '12'); + svg.setAttribute('height', '12'); + svg.setAttribute('aria-hidden', 'true'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', 'M2 4 L6 8 L10 4'); + path.setAttribute('fill', 'none'); + path.setAttribute('stroke', 'currentColor'); + path.setAttribute('stroke-width', '1.5'); + path.setAttribute('stroke-linecap', 'round'); + path.setAttribute('stroke-linejoin', 'round'); + svg.appendChild(path); + return svg; + } + + setItems(items: DropdownItem[]): void { + this.items = items; + this.renderItems(); + } + + setLabel(label: string): void { + this.labelEl.textContent = label; + } + + setValue(value: T): void { + this.currentValue = value; + this.menu.querySelectorAll('.at-dropdown-item').forEach(el => { + const v = (el as HTMLElement).dataset.value; + el.classList.toggle('active', v === String(value)); + }); + } + + setEnabled(enabled: boolean): void { + this.toggle.disabled = !enabled; + } + + private async open(): Promise { + if (this.props.onOpen) { + const result = await this.props.onOpen(); + if (Array.isArray(result)) { + this.setItems(result); + } + } + if (!this.menu.parentElement) { + document.body.appendChild(this.menu); + } + if (!this.popper) { + this.popper = createPopper(this.toggle, this.menu, { + placement: this.props.placement === 'bottom' ? 'bottom-start' : 'top-start', + modifiers: [{ name: 'offset', options: { offset: [0, 4] } }] + }); + } else { + this.popper.update(); + } + this.isOpen = true; + this.root.classList.add('open'); + this.menu.classList.add('open'); + this.toggle.setAttribute('aria-expanded', 'true'); + document.addEventListener('mousedown', this.outsideClick, true); + document.addEventListener('keydown', this.onKey, true); + } + + private close(): void { + this.isOpen = false; + this.root.classList.remove('open'); + this.menu.classList.remove('open'); + this.toggle.setAttribute('aria-expanded', 'false'); + document.removeEventListener('mousedown', this.outsideClick, true); + document.removeEventListener('keydown', this.onKey, true); + } + + private renderItems(): void { + this.menu.replaceChildren(); + for (const item of this.items) { + const el = parseHtml(html``); + el.dataset.value = String(item.value); + if (item.icon) { + const iconWrap = document.createElement('span'); + iconWrap.className = 'at-dropdown-item-icon'; + iconWrap.appendChild(renderIcon(item.icon)); + el.appendChild(iconWrap); + } + const labelWrap = document.createElement('span'); + labelWrap.className = 'at-dropdown-item-label'; + labelWrap.textContent = item.label; + el.appendChild(labelWrap); + if (this.currentValue !== undefined && String(this.currentValue) === String(item.value)) { + el.classList.add('active'); + } + el.addEventListener('click', e => { + e.stopPropagation(); + this.setValue(item.value); + this.close(); + this.onSelect?.(item.value, item); + }); + this.menu.appendChild(el); + } + } + + dispose(): void { + this.close(); + this.tooltip?.dispose(); + this.popper?.destroy(); + this.menu.remove(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/IconButton.ts b/packages/playground/src/components/primitives/IconButton.ts new file mode 100644 index 000000000..1c8ab8458 --- /dev/null +++ b/packages/playground/src/components/primitives/IconButton.ts @@ -0,0 +1,94 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; +import type { IconNode } from '../../util/Icons'; +import { icon as renderIcon } from '../../util/Icons'; +import { Tooltip } from './Tooltip'; + +injectStyles( + 'IconButton', + css` + .at-icon-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 4px; + padding: 6px 10px; + height: 40px; + min-width: 40px; + border: 0; + background: transparent; + color: inherit; + cursor: pointer; + border-radius: 0; + font: inherit; + transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out; + } + .at-icon-btn:hover:not([disabled]) { background: var(--at-accent-hover); } + .at-icon-btn[disabled] { opacity: 0.4; cursor: default; } + .at-icon-btn .at-icon-btn-icon { display: inline-flex; } + .at-icon-btn .at-icon-btn-icon > svg { width: 16px; height: 16px; } + .at-icon-btn .at-icon-btn-label { font-size: 12px; } + .at-icon-btn .at-icon-btn-label:empty { display: none; } +` +); + +export interface IconButtonProps { + icon: IconNode; + tooltip?: string; + label?: string; + ariaLabel?: string; +} + +export class IconButton implements Mountable { + readonly root: HTMLButtonElement; + private iconSlot: HTMLElement; + private labelSlot: HTMLElement; + private tooltip: Tooltip | null = null; + + onClick: ((e: MouseEvent) => void) | null = null; + + constructor(props: IconButtonProps) { + this.root = parseHtml(html` + + `) as HTMLButtonElement; + this.iconSlot = this.root.querySelector('.at-icon-btn-icon')!; + this.labelSlot = this.root.querySelector('.at-icon-btn-label')!; + this.setIcon(props.icon); + if (props.tooltip) { + this.tooltip = new Tooltip(this.root, props.tooltip); + } + this.root.addEventListener('click', e => { + if (this.root.disabled) { + return; + } + this.onClick?.(e); + }); + } + + setIcon(node: IconNode): void { + this.iconSlot.replaceChildren(renderIcon(node)); + } + + setLabel(label: string): void { + this.labelSlot.textContent = label; + } + + setTooltip(text: string): void { + if (this.tooltip) { + this.tooltip.setText(text); + } else if (text) { + this.tooltip = new Tooltip(this.root, text); + } + } + + setEnabled(enabled: boolean): void { + this.root.disabled = !enabled; + } + + dispose(): void { + this.tooltip?.dispose(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/LoadingProgress.ts b/packages/playground/src/components/primitives/LoadingProgress.ts new file mode 100644 index 000000000..53a0c5ca4 --- /dev/null +++ b/packages/playground/src/components/primitives/LoadingProgress.ts @@ -0,0 +1,97 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'LoadingProgress', + css` + .at-loading-progress { + position: relative; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 8px; + color: inherit; + } + .at-loading-progress::after { + content: ''; + position: absolute; + inset: 0; + border-radius: 50%; + border: 3px solid #eee; + } + .at-loading-progress > .at-loading-progress-half { + position: absolute; + top: 0; + width: 50%; + height: 100%; + overflow: hidden; + } + .at-loading-progress > .at-loading-progress-half.left { left: 0; } + .at-loading-progress > .at-loading-progress-half.right { right: 0; } + .at-loading-progress > .at-loading-progress-half > .at-loading-progress-arc { + position: absolute; + top: 0; + width: 100%; + height: 100%; + border: 3px solid var(--at-accent); + } + .at-loading-progress > .at-loading-progress-half.left > .at-loading-progress-arc { + left: 100%; + border-top-right-radius: 16px; + border-bottom-right-radius: 16px; + border-left: 0; + transform-origin: center left; + transform: rotate(0deg); + } + .at-loading-progress > .at-loading-progress-half.right > .at-loading-progress-arc { + left: -100%; + border-top-left-radius: 16px; + border-bottom-left-radius: 16px; + border-right: 0; + transform-origin: center right; + transform: rotate(0deg); + } + .at-loading-progress > .at-loading-progress-value { + position: relative; + z-index: 1; + } +` +); + +export class LoadingProgress implements Mountable { + readonly root: HTMLElement; + private leftArc: HTMLElement; + private rightArc: HTMLElement; + private valueLabel: HTMLElement; + + constructor() { + this.root = parseHtml(html` +
+ + + 0 +
+ `); + this.leftArc = this.root.querySelector('.left > .at-loading-progress-arc')!; + this.rightArc = this.root.querySelector('.right > .at-loading-progress-arc')!; + this.valueLabel = this.root.querySelector('.at-loading-progress-value')!; + } + + setValue(value: number): void { + const percent = Math.max(0, Math.min(1, value)) * 100; + if (percent <= 50) { + this.rightArc.style.transform = `rotate(${(percent / 100) * 360}deg)`; + this.leftArc.style.transform = 'rotate(0deg)'; + } else { + this.rightArc.style.transform = 'rotate(180deg)'; + this.leftArc.style.transform = `rotate(${((percent - 50) / 100) * 360}deg)`; + } + this.valueLabel.textContent = String(Math.floor(percent)); + this.root.setAttribute('aria-valuenow', String(Math.floor(percent))); + } + + dispose(): void { + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/ProgressBar.ts b/packages/playground/src/components/primitives/ProgressBar.ts new file mode 100644 index 000000000..85b2d8644 --- /dev/null +++ b/packages/playground/src/components/primitives/ProgressBar.ts @@ -0,0 +1,51 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'ProgressBar', + css` + .at-progress-bar { + position: relative; + height: 4px; + background: #d9d9d9; + cursor: pointer; + transition: height 0.1s ease; + } + .at-progress-bar:hover { height: 15px; } + .at-progress-bar > .at-progress-bar-fill { + height: 100%; + background: var(--at-accent); + width: 0; + } +` +); + +export class ProgressBar implements Mountable { + readonly root: HTMLElement; + private fill: HTMLElement; + + onClickPercent: ((percent: number) => void) | null = null; + + constructor() { + this.root = parseHtml(html` +
+
+
+ `); + this.fill = this.root.querySelector('.at-progress-bar-fill')!; + this.root.addEventListener('click', e => { + const rect = this.root.getBoundingClientRect(); + const percent = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); + this.onClickPercent?.(percent); + }); + } + + setValue(value: number): void { + const v = Math.max(0, Math.min(1, value)); + this.fill.style.width = `${(v * 100).toFixed(2)}%`; + this.root.setAttribute('aria-valuenow', v.toFixed(3)); + } + + dispose(): void { + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/Slider.ts b/packages/playground/src/components/primitives/Slider.ts new file mode 100644 index 000000000..a3696d4db --- /dev/null +++ b/packages/playground/src/components/primitives/Slider.ts @@ -0,0 +1,74 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'Slider', + css` + .at-slider { + appearance: none; + background: #d3d3d3; + outline: none; + opacity: 0.7; + transition: opacity 0.2s; + height: 5px; + margin: 0; + } + .at-slider:hover { opacity: 1; } + .at-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--at-accent); + cursor: pointer; + border: 0; + } + .at-slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--at-accent); + cursor: pointer; + border: 0; + } +` +); + +export interface SliderProps { + min?: number; + max?: number; + step?: number; + initialValue?: number; +} + +export class Slider implements Mountable { + readonly root: HTMLInputElement; + + onInput: ((value: number) => void) | null = null; + + constructor(props: SliderProps = {}) { + const min = props.min ?? 0; + const max = props.max ?? 100; + const step = props.step ?? 1; + const value = props.initialValue ?? min; + this.root = parseHtml(html` + + `) as HTMLInputElement; + this.root.addEventListener('input', () => { + this.onInput?.(this.root.valueAsNumber); + }); + this.root.addEventListener('click', e => e.stopPropagation()); + } + + getValue(): number { + return this.root.valueAsNumber; + } + + setValue(value: number): void { + this.root.valueAsNumber = value; + } + + dispose(): void { + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/Spinner.ts b/packages/playground/src/components/primitives/Spinner.ts new file mode 100644 index 000000000..e6e057c6e --- /dev/null +++ b/packages/playground/src/components/primitives/Spinner.ts @@ -0,0 +1,36 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'Spinner', + css` + @keyframes at-spinner-rotate { to { transform: rotate(360deg); } } + .at-spinner { + display: inline-block; + width: 3rem; + height: 3rem; + vertical-align: middle; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: at-spinner-rotate 0.75s linear infinite; + color: var(--at-accent); + } + .at-spinner.small { width: 1rem; height: 1rem; border-width: 0.15em; } +` +); + +export interface SpinnerProps { + small?: boolean; +} + +export class Spinner implements Mountable { + readonly root: HTMLElement; + + constructor(props: SpinnerProps = {}) { + this.root = parseHtml(html`
`); + } + + dispose(): void { + this.root.remove(); + } +} diff --git a/packages/playground/src/components/primitives/ToggleButton.ts b/packages/playground/src/components/primitives/ToggleButton.ts new file mode 100644 index 000000000..1f2d0e5ea --- /dev/null +++ b/packages/playground/src/components/primitives/ToggleButton.ts @@ -0,0 +1,45 @@ +import { css, injectStyles } from '../../util/Dom'; +import { IconButton, type IconButtonProps } from './IconButton'; + +injectStyles( + 'ToggleButton', + css` + .at-icon-btn.at-toggle-active { + background: var(--at-accent-hover); + color: #fff; + } +` +); + +export interface ToggleButtonProps extends IconButtonProps { + initialActive?: boolean; +} + +export class ToggleButton extends IconButton { + private active: boolean; + + onChange: ((active: boolean) => void) | null = null; + + constructor(props: ToggleButtonProps) { + super(props); + this.active = props.initialActive ?? false; + this.root.classList.add('at-toggle'); + this.root.classList.toggle('at-toggle-active', this.active); + this.root.setAttribute('aria-pressed', String(this.active)); + + this.onClick = () => { + this.setActive(!this.active); + this.onChange?.(this.active); + }; + } + + isActive(): boolean { + return this.active; + } + + setActive(active: boolean): void { + this.active = active; + this.root.classList.toggle('at-toggle-active', active); + this.root.setAttribute('aria-pressed', String(active)); + } +} diff --git a/packages/playground/src/components/primitives/Tooltip.ts b/packages/playground/src/components/primitives/Tooltip.ts new file mode 100644 index 000000000..72531a033 --- /dev/null +++ b/packages/playground/src/components/primitives/Tooltip.ts @@ -0,0 +1,89 @@ +import { createPopper, type Instance as PopperInstance } from '@popperjs/core'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'Tooltip', + css` + .at-tooltip { + position: fixed; + top: 0; + left: 0; + z-index: 20000; + background: rgba(33, 37, 41, 0.95); + color: #fff; + padding: 4px 8px; + font-size: 12px; + border-radius: 4px; + pointer-events: none; + opacity: 0; + transition: opacity 0.12s ease-in-out; + max-width: 220px; + white-space: nowrap; + } + .at-tooltip.visible { opacity: 1; } +` +); + +export interface TooltipOptions { + placement?: 'top' | 'bottom' | 'left' | 'right'; +} + +export class Tooltip implements Mountable { + readonly root: HTMLElement; + private popper: PopperInstance | null = null; + private mouseEnter = () => this.show(); + private mouseLeave = () => this.hide(); + private focusIn = () => this.show(); + private focusOut = () => this.hide(); + + constructor( + private target: HTMLElement, + private text: string, + private options: TooltipOptions = {} + ) { + this.root = parseHtml(html``); + this.target.addEventListener('mouseenter', this.mouseEnter); + this.target.addEventListener('mouseleave', this.mouseLeave); + this.target.addEventListener('focusin', this.focusIn); + this.target.addEventListener('focusout', this.focusOut); + } + + setText(text: string): void { + this.text = text; + this.root.textContent = text; + } + + private show(): void { + if (!this.text) { + return; + } + if (!this.root.parentElement) { + document.body.appendChild(this.root); + } + if (!this.popper) { + this.popper = createPopper(this.target, this.root, { + placement: this.options.placement ?? 'top', + modifiers: [{ name: 'offset', options: { offset: [0, 6] } }] + }); + } else { + this.popper.update(); + } + this.root.classList.add('visible'); + } + + private hide(): void { + this.root.classList.remove('visible'); + } + + dispose(): void { + this.target.removeEventListener('mouseenter', this.mouseEnter); + this.target.removeEventListener('mouseleave', this.mouseLeave); + this.target.removeEventListener('focusin', this.focusIn); + this.target.removeEventListener('focusout', this.focusOut); + if (this.popper) { + this.popper.destroy(); + this.popper = null; + } + this.root.remove(); + } +} diff --git a/packages/playground/src/components/recorder/DrumPadPanel.ts b/packages/playground/src/components/recorder/DrumPadPanel.ts new file mode 100644 index 000000000..132985555 --- /dev/null +++ b/packages/playground/src/components/recorder/DrumPadPanel.ts @@ -0,0 +1,191 @@ +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'DrumPadPanel', + css` + .at-drum-pad-panel { + position: fixed; + bottom: 72px; + right: 16px; + padding: 0 12px 12px 12px; + background: rgba(33, 37, 41, 0.92); + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); + z-index: 1000; + color: #fff; + font-family: system-ui, sans-serif; + } + .at-drum-pad-panel > .at-drum-pad-handle { + padding: 8px 10px; + margin-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.15); + cursor: move; + user-select: none; + font-size: 11px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + } + .at-drum-pad-panel .at-drum-pad-state-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + background: #6c757d; + } + .at-drum-pad-panel.recording .at-drum-pad-state-dot { + background: #e53935; + box-shadow: 0 0 6px #e53935; + } + .at-drum-pad-panel > .at-drum-pad-grid { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 8px; + opacity: 0.4; + pointer-events: none; + } + .at-drum-pad-panel.recording > .at-drum-pad-grid { + opacity: 1; + pointer-events: auto; + } + .at-drum-pad-panel .at-drum-pad-btn { + padding: 8px 10px; + min-width: 68px; + background: #4a5056; + color: #fff; + border: 1px solid #6c757d; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + line-height: 1.2; + white-space: pre; + font-family: inherit; + transition: background 0.12s ease-out, border-color 0.12s ease-out; + } + .at-drum-pad-panel .at-drum-pad-btn.flash { + background: #ff7043; + border-color: #ffab91; + } +` +); + +export interface DrumPad { + key: string; + midiNote: number; + label: string; +} + +export const DEFAULT_DRUM_PADS: DrumPad[] = [ + { key: 'a', midiNote: 36, label: 'Kick' }, + { key: 's', midiNote: 38, label: 'Snare' }, + { key: 'd', midiNote: 42, label: 'Hi-Hat' }, + { key: 'f', midiNote: 46, label: 'Open HH' }, + { key: 'g', midiNote: 44, label: 'HH Pedal' }, + { key: 'h', midiNote: 45, label: 'Low Tom' }, + { key: 'j', midiNote: 47, label: 'Mid Tom' }, + { key: 'k', midiNote: 50, label: 'Hi Tom' }, + { key: 'l', midiNote: 49, label: 'Crash' }, + { key: ';', midiNote: 51, label: 'Ride' } +]; + +const FLASH_MS = 120; + +export class DrumPadPanel implements Mountable { + readonly root: HTMLElement; + private gridEl: HTMLElement; + private handleEl: HTMLElement; + private stateLabelEl: HTMLElement; + private btnByMidi = new Map(); + private padByKey = new Map(); + + private dragOffsetX = 0; + private dragOffsetY = 0; + private onPointerDown = (e: PointerEvent) => { + const rect = this.root.getBoundingClientRect(); + this.dragOffsetX = e.clientX - rect.left; + this.dragOffsetY = e.clientY - rect.top; + this.root.style.left = `${rect.left}px`; + this.root.style.top = `${rect.top}px`; + this.root.style.right = 'auto'; + this.root.style.bottom = 'auto'; + this.handleEl.setPointerCapture(e.pointerId); + }; + private onPointerMove = (e: PointerEvent) => { + if (!this.handleEl.hasPointerCapture(e.pointerId)) { + return; + } + this.root.style.left = `${e.clientX - this.dragOffsetX}px`; + this.root.style.top = `${e.clientY - this.dragOffsetY}px`; + }; + private onPointerUp = (e: PointerEvent) => { + this.handleEl.releasePointerCapture(e.pointerId); + }; + private onKeyDown = (e: KeyboardEvent) => { + if (e.repeat) { + return; + } + const pad = this.padByKey.get(e.key.toLowerCase()); + if (!pad) { + return; + } + e.preventDefault(); + this.onHit?.(pad.midiNote); + }; + + onHit: ((midiNote: number) => void) | null = null; + + constructor(pads: DrumPad[] = DEFAULT_DRUM_PADS) { + this.root = parseHtml(html` +
+
+ + Paused — press Play to record +
+
+
+ `); + this.handleEl = this.root.querySelector('.at-drum-pad-handle')!; + this.stateLabelEl = this.root.querySelector('.at-drum-pad-state-label')!; + this.gridEl = this.root.querySelector('.at-drum-pad-grid')!; + + for (const pad of pads) { + this.padByKey.set(pad.key, pad); + const btn = parseHtml( + html`` + ) as HTMLButtonElement; + // textContent preserves the literal newline + btn.textContent = `${pad.label}\n[${pad.key.toUpperCase()}]`; + btn.addEventListener('click', () => this.onHit?.(pad.midiNote)); + this.gridEl.appendChild(btn); + this.btnByMidi.set(pad.midiNote, btn); + } + + this.handleEl.addEventListener('pointerdown', this.onPointerDown); + this.handleEl.addEventListener('pointermove', this.onPointerMove); + this.handleEl.addEventListener('pointerup', this.onPointerUp); + document.addEventListener('keydown', this.onKeyDown, true); + } + + setRecording(recording: boolean): void { + this.root.classList.toggle('recording', recording); + this.stateLabelEl.textContent = recording + ? 'Recording — hit pads or press keys' + : 'Paused — press Play to record'; + } + + flash(midiNote: number): void { + const btn = this.btnByMidi.get(midiNote); + if (!btn) { + return; + } + btn.classList.add('flash'); + window.setTimeout(() => btn.classList.remove('flash'), FLASH_MS); + } + + dispose(): void { + document.removeEventListener('keydown', this.onKeyDown, true); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/recorder/RhythmConfig.ts b/packages/playground/src/components/recorder/RhythmConfig.ts new file mode 100644 index 000000000..537218f98 --- /dev/null +++ b/packages/playground/src/components/recorder/RhythmConfig.ts @@ -0,0 +1,18 @@ +export interface RhythmConfig { + timeSignatureNumerator: number; + timeSignatureDenominator: number; + /** Grid slots inside one beat (quarter). */ + subdivisionsPerBeat: number; + /** Length = numerator * subdivisionsPerBeat. Values are display weights (4=quarter, 8=eighth, 16=16th, 32=32nd). */ + beatMask: number[]; + /** Slots whose weight exceeds this threshold are not drawn in the overlay. */ + displayWeightThreshold: number; +} + +export const RHYTHM_4_4_STRAIGHT: RhythmConfig = { + timeSignatureNumerator: 4, + timeSignatureDenominator: 4, + subdivisionsPerBeat: 4, + beatMask: Array.from({ length: 4 }, () => [4, 16, 8, 16]).flat(), + displayWeightThreshold: 16 +}; diff --git a/packages/playground/src/components/recorder/RhythmGridOverlay.ts b/packages/playground/src/components/recorder/RhythmGridOverlay.ts new file mode 100644 index 000000000..7041e888b --- /dev/null +++ b/packages/playground/src/components/recorder/RhythmGridOverlay.ts @@ -0,0 +1,92 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; +import type { RhythmConfig } from './RhythmConfig'; + +injectStyles( + 'RhythmGridOverlay', + css` + .at-rhythm-grid { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 6; + } + .at-rhythm-grid > .at-rhythm-tick { + position: absolute; + } + .at-rhythm-grid > .at-rhythm-tick.main { + background: rgba(0, 120, 220, 0.55); + } + .at-rhythm-grid > .at-rhythm-tick.sub { + background: rgba(0, 120, 220, 0.25); + } +` +); + +const TICK_LENGTH = 12; + +export class RhythmGridOverlay implements Mountable { + readonly root: HTMLElement; + private unsubPostRender: () => void; + + constructor( + private api: alphaTab.AlphaTabApi, + private config: RhythmConfig + ) { + this.root = parseHtml(html`
`); + this.unsubPostRender = api.postRenderFinished.on(() => this.redraw()); + } + + setConfig(config: RhythmConfig): void { + this.config = config; + this.redraw(); + } + + private redraw(): void { + this.root.replaceChildren(); + const lookup = this.api.renderer?.boundsLookup; + if (!lookup) { + return; + } + for (const system of lookup.staffSystems) { + for (const masterBarBounds of system.bars) { + for (const barBounds of masterBarBounds.bars) { + const beats = barBounds.beats; + const barTop = barBounds.realBounds.y; + const barBottom = barBounds.realBounds.y + barBounds.realBounds.h; + for (let i = 0; i < beats.length && i < this.config.beatMask.length; i++) { + const weight = this.config.beatMask[i]; + if (weight > this.config.displayWeightThreshold) { + continue; + } + const isMain = weight === 4; + const width = isMain ? 2 : 1; + const x = beats[i].realBounds.x; + const cls = isMain ? 'at-rhythm-tick main' : 'at-rhythm-tick sub'; + + const top = document.createElement('div'); + top.className = cls; + top.style.left = `${x}px`; + top.style.top = `${barTop - TICK_LENGTH}px`; + top.style.width = `${width}px`; + top.style.height = `${TICK_LENGTH}px`; + this.root.appendChild(top); + + const bottom = document.createElement('div'); + bottom.className = cls; + bottom.style.left = `${x}px`; + bottom.style.top = `${barBottom}px`; + bottom.style.width = `${width}px`; + bottom.style.height = `${TICK_LENGTH}px`; + this.root.appendChild(bottom); + } + } + } + } + } + + dispose(): void { + this.unsubPostRender(); + this.root.remove(); + } +} diff --git a/packages/playground/src/components/youtube-sync/YoutubeSync.ts b/packages/playground/src/components/youtube-sync/YoutubeSync.ts new file mode 100644 index 000000000..622ad5c37 --- /dev/null +++ b/packages/playground/src/components/youtube-sync/YoutubeSync.ts @@ -0,0 +1,191 @@ +import type * as alphaTab from '@coderline/alphatab'; +import { type Mountable, css, html, injectStyles, parseHtml } from '../../util/Dom'; + +injectStyles( + 'YoutubeSync', + css` + .at-youtube-wrap { + display: flex; + justify-content: center; + height: 360px; + background: #000; + } + .at-youtube-wrap > .at-youtube { width: 640px; height: 360px; } +` +); + +// Minimal YouTube IFrame API surface used by this component. +interface YTPlayer { + getDuration(): number; + getPlaybackRate(): number; + setPlaybackRate(value: number): void; + getVolume(): number; + setVolume(value: number): void; + seekTo(seconds: number): void; + playVideo(): void; + pauseVideo(): void; + getCurrentTime(): number; + destroy?(): void; +} +interface YTEvent { + data: number; +} +interface YTPlayerCtor { + new ( + host: HTMLElement, + options: { + height: string; + width: string; + videoId: string; + playerVars?: { autoplay?: 0 | 1 }; + events?: { + onReady?: (e: YTEvent) => void; + onStateChange?: (e: YTEvent) => void; + onPlaybackRateChange?: (e: YTEvent) => void; + onError?: (e: YTEvent) => void; + }; + } + ): YTPlayer; +} +interface YTNamespace { + Player: YTPlayerCtor; +} + +declare global { + interface Window { + YT?: YTNamespace; + onYouTubePlayerAPIReady?: () => void; + } +} + +const YT_API_URL = 'https://www.youtube.com/player_api'; +let ytApiPromise: Promise | null = null; + +function loadYouTubeApi(): Promise { + if (ytApiPromise) { + return ytApiPromise; + } + ytApiPromise = new Promise((resolve, reject) => { + if (window.YT?.Player) { + resolve(); + return; + } + const previous = window.onYouTubePlayerAPIReady; + window.onYouTubePlayerAPIReady = () => { + previous?.(); + resolve(); + }; + const tag = document.createElement('script'); + tag.src = YT_API_URL; + tag.onerror = e => reject(e); + document.head.appendChild(tag); + }); + return ytApiPromise; +} + +export interface YoutubeSyncProps { + videoId: string; +} + +export class YoutubeSync implements Mountable { + readonly root: HTMLElement; + private playerEl: HTMLElement; + private player: YTPlayer | null = null; + private currentTimeInterval: number | undefined; + + constructor( + private api: alphaTab.AlphaTabApi, + private props: YoutubeSyncProps + ) { + this.root = parseHtml(html` +
+
+
+ `); + this.playerEl = this.root.querySelector('.at-youtube')!; + this.bind(); + } + + private async bind(): Promise { + await loadYouTubeApi(); + const ready = Promise.withResolvers(); + this.player = new window.YT!.Player(this.playerEl, { + height: '360', + width: '640', + videoId: this.props.videoId, + playerVars: { autoplay: 0 }, + events: { + onReady: () => ready.resolve(), + onStateChange: e => this.onStateChange(e.data), + onPlaybackRateChange: e => { + this.api.playbackSpeed = e.data; + }, + onError: e => ready.reject(e) + } + }); + await ready.promise; + + const output = this.api.player?.output as alphaTab.synth.IExternalMediaSynthOutput | undefined; + if (!output) { + return; + } + const player = this.player!; + const handler: alphaTab.synth.IExternalMediaHandler = { + get backingTrackDuration() { + return player.getDuration() * 1000; + }, + get playbackRate() { + return player.getPlaybackRate(); + }, + set playbackRate(value: number) { + player.setPlaybackRate(value); + }, + get masterVolume() { + return player.getVolume() / 100; + }, + set masterVolume(value: number) { + player.setVolume(value * 100); + }, + seekTo: time => player.seekTo(time / 1000), + play: () => player.playVideo(), + pause: () => player.pauseVideo() + }; + output.handler = handler; + } + + private onStateChange(state: number): void { + // YT.PlayerState: -1=unstarted, 0=ended, 1=playing, 2=paused, 3=buffering, 5=cued + switch (state) { + case 1: // playing + this.currentTimeInterval = window.setInterval(() => { + const output = this.api.player?.output as alphaTab.synth.IExternalMediaSynthOutput | undefined; + if (output && this.player) { + output.updatePosition(this.player.getCurrentTime() * 1000); + } + }, 50); + this.api.play(); + break; + case 0: // ended + this.clearInterval(); + this.api.stop(); + break; + case 2: // paused + this.clearInterval(); + this.api.pause(); + break; + } + } + + private clearInterval(): void { + if (this.currentTimeInterval !== undefined) { + window.clearInterval(this.currentTimeInterval); + this.currentTimeInterval = undefined; + } + } + + dispose(): void { + this.clearInterval(); + this.player?.destroy?.(); + this.root.remove(); + } +} diff --git a/packages/playground/src/styles/common.css b/packages/playground/src/styles/common.css new file mode 100644 index 000000000..d3994198d --- /dev/null +++ b/packages/playground/src/styles/common.css @@ -0,0 +1,83 @@ +@import url('@fontsource/noto-sans/300.css'); +@import url('@fontsource/noto-sans/400.css'); +@import url('@fontsource/noto-sans/500.css'); +@import url('@fontsource/noto-sans/700.css'); + +@import url('@fontsource/noto-sans/300-italic.css'); +@import url('@fontsource/noto-sans/400-italic.css'); +@import url('@fontsource/noto-sans/500-italic.css'); +@import url('@fontsource/noto-sans/700-italic.css'); + +@import url('@fontsource/noto-serif/300.css'); +@import url('@fontsource/noto-serif/400.css'); +@import url('@fontsource/noto-serif/500.css'); +@import url('@fontsource/noto-serif/700.css'); + +@import url('@fontsource/noto-serif/300-italic.css'); +@import url('@fontsource/noto-serif/400-italic.css'); +@import url('@fontsource/noto-serif/500-italic.css'); +@import url('@fontsource/noto-serif/700-italic.css'); + +:root { + --at-accent: #4972a1; + --at-accent-hover: #5588c7; + --at-footer-bg: #436d9d; + --at-footer-fg: #ffffff; + --at-divider: rgba(255, 255, 255, 0.18); + --at-sidebar-bg: #f7f7f7; + --at-overlay-bg: rgba(0, 0, 0, 0.5); + --at-track-active-bg: rgba(0, 0, 0, 0.03); + --at-cursor-beat: rgba(64, 64, 255, 0.75); + --at-cursor-bar: rgba(255, 242, 0, 0.25); + --at-selection: rgba(64, 64, 255, 0.2); + --at-highlight: #0078ff; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; + font-family: 'Noto Sans', sans-serif; + font-size: 14px; + height: 100vh; +} + +body { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + flex-wrap: wrap; +} + +body > * { + overflow: hidden; +} + +button { + font-family: inherit; +} + +.at-cursor-bar { + background: var(--at-cursor-bar); +} + +.at-selection div { + background: var(--at-selection); +} + +.at-cursor-beat { + background: var(--at-cursor-beat); + width: 3px; +} + +.at-highlight * { + fill: var(--at-highlight); + stroke: var(--at-highlight); +} diff --git a/packages/playground/src/util/Demos.ts b/packages/playground/src/util/Demos.ts new file mode 100644 index 000000000..a972bbcc2 --- /dev/null +++ b/packages/playground/src/util/Demos.ts @@ -0,0 +1,38 @@ +export interface DemoEntry { + href: string; + title: string; + description: string; +} + +export const DEMOS: DemoEntry[] = [ + { + href: '/demos/control/', + title: 'Control', + description: + 'The full-featured player: track sidebar, transport bar, layout/scroll/zoom pickers, downloads, drag-drop loading.' + }, + { + href: '/demos/recorder/', + title: 'Drum Recorder', + description: + 'Record drum hits onto a percussion staff while the player runs. Demonstrates dynamic score extension and tick-cache updates.' + }, + { + href: '/demos/alphatex-editor/', + title: 'AlphaTex Editor', + description: + 'Monaco editor on the left, live alphaTab rendering on the right. Round-trips between AlphaTex source and Score model with LSP support.' + }, + { + href: '/demos/youtube-sync/', + title: 'YouTube Sync', + description: + 'Plays an alphaTab score in step with a YouTube video using the EnabledExternalMedia player mode.' + }, + { + href: '/demos/test-results/', + title: 'Visual Test Results', + description: + 'Compare expected vs. actual screenshots from visual regression runs. Drop a results zip or use the dev-server endpoint.' + } +]; diff --git a/packages/playground/src/util/Dom.ts b/packages/playground/src/util/Dom.ts new file mode 100644 index 000000000..22ec2c478 --- /dev/null +++ b/packages/playground/src/util/Dom.ts @@ -0,0 +1,66 @@ +export interface Mountable { + readonly root: HTMLElement; +} + +export function escapeHtml(value: unknown): string { + return String(value).replace(/[&<>"']/g, c => { + switch (c) { + case '&': + return '&'; + case '<': + return '<'; + case '>': + return '>'; + case '"': + return '"'; + case "'": + return '''; + default: + return c; + } + }); +} + +export function html(strings: TemplateStringsArray, ...values: unknown[]): string { + return String.raw({ raw: strings }, ...values.map(v => escapeHtml(v))); +} + +export function css(strings: TemplateStringsArray, ...values: unknown[]): string { + return String.raw({ raw: strings }, ...values); +} + +export function parseHtml(markup: string): HTMLElement { + const t = document.createElement('template'); + t.innerHTML = markup.trim(); + const el = t.content.firstElementChild; + if (!(el instanceof HTMLElement)) { + throw new Error('parseHtml: template did not produce an HTMLElement'); + } + //