chore(deps): SvelteKit 3 with adapter-static 4 and TypeScript 6, full-tree npm audit (#5021)
Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps, plus the migration they need. The mechanical part is `sv migrate sveltekit-3`, run one task at a time and reviewed: - svelte.config.js is gone. Its options move into sveltekit() in vite.config.ts, exported as kitOptions so vitest.config.ts runs the same kit setup, including the $test-utils alias the tests import. - $lib becomes #lib through package.json "imports". There is no src/lib/index, so only the "#lib/*" entry is kept. - tsconfig extends $app/tsconfig. - Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite ^8.0.12, svelte-check ^4.7.5. By hand, from the codemod's list of non-automated tasks: - goto's replaceState option is now replace; keepFocus becomes reset: false. For the search typeahead, reset: false also stops the scroll-to-top, which is wanted while typing. - The test setup mocks drop pushState/replaceState and $app/paths base/assets, which kit 3 removed, and mock refreshAll in place of invalidateAll. - The other flagged files only read page.url or goto internal routes, so they needed no change. TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to 7 once both accept it. With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0, so the web lane now audits every dependency rather than only what ships to browsers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -295,12 +295,13 @@ jobs:
|
|||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
# What ships to browsers: `dependencies` and the runtime they pull in
|
# The whole tree, build and test tooling included. Until #5021 this
|
||||||
# (svelte, devalue). Build and test tooling (vite, vitest, tailwind,
|
# audited only what ships to browsers (`--omit=dev`), because vite,
|
||||||
# kit's dev server) is left out because none of it reaches a user, and
|
# vitest, tailwind and kit carried advisories that needed major
|
||||||
# its open advisories need major-version upgrades tracked separately.
|
# upgrades. Those upgrades landed and the full tree audits clean, so
|
||||||
- name: npm audit (shipped dependencies)
|
# the tooling that builds the shipped bundle is held to the same bar.
|
||||||
run: npm audit --omit=dev --audit-level=moderate
|
- name: npm audit (all dependencies)
|
||||||
|
run: npm audit --audit-level=moderate
|
||||||
|
|
||||||
- name: Type-check + svelte-check
|
- name: Type-check + svelte-check
|
||||||
run: npm run check
|
run: npm run check
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ Keystore, so a copy of the app's files doesn't yield a usable session.
|
|||||||
|
|
||||||
Nothing is published unless every check passes: the Go, integration, web and
|
Nothing is published unless every check passes: the Go, integration, web and
|
||||||
Android test suites, `govulncheck` against the toolchain that builds the
|
Android test suites, `govulncheck` against the toolchain that builds the
|
||||||
image, and `npm audit` on the packages that ship to the browser. See
|
image, and `npm audit` on every web dependency, build tooling included. See
|
||||||
`.gitea/workflows/release.yml`.
|
`.gitea/workflows/release.yml`.
|
||||||
|
|
||||||
## Reporting a problem
|
## Reporting a problem
|
||||||
|
|||||||
Generated
+61
-65
@@ -13,8 +13,8 @@
|
|||||||
"lucide-svelte": "^1.0.1"
|
"lucide-svelte": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^4.0.0",
|
||||||
"@sveltejs/kit": "^2.8.0",
|
"@sveltejs/kit": "^3.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@tailwindcss/postcss": "^4.3.3",
|
"@tailwindcss/postcss": "^4.3.3",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.1",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^25.0.1",
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.15",
|
||||||
"svelte": "^5.1.9",
|
"svelte": "^5.57.1",
|
||||||
"svelte-check": "^4.4.8",
|
"svelte-check": "^4.7.5",
|
||||||
"tailwindcss": "^4.3.3",
|
"tailwindcss": "^4.3.3",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^6.0.0",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.12",
|
||||||
"vitest": "^5.0.0"
|
"vitest": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -987,46 +987,43 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sveltejs/adapter-static": {
|
"node_modules/@sveltejs/adapter-static": {
|
||||||
"version": "3.0.10",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-4.0.0.tgz",
|
||||||
"integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==",
|
"integrity": "sha512-aF1J23R1ebKqUxQ4/qLr/Lxu63NFmLAtg8UZl3HFLxaoA1D/nhTVGcD9lTqx65iUbjNR2iOL0TgOxSX82x6Paw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@sveltejs/kit": "^2.0.0"
|
"@sveltejs/kit": "^3.0.0-next.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sveltejs/kit": {
|
"node_modules/@sveltejs/kit": {
|
||||||
"version": "2.70.3",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-3.0.1.tgz",
|
||||||
"integrity": "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==",
|
"integrity": "sha512-8JMCCq7ZUJELi3hbhb0+c9Rs8qnbHW90vhOfM+t1FSF4fhIpG9/FOsH4IYgcITMhbdkSrrn+Kvd9G+q8A+UO6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@rolldown/pluginutils": "^1.0.1",
|
||||||
"@sveltejs/acorn-typescript": "^1.0.9",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"@types/cookie": "^0.6.0",
|
"cookie": "^2.0.1",
|
||||||
"acorn": "^8.16.0",
|
"devalue": "^5.9.4",
|
||||||
"cookie": "^0.6.0",
|
|
||||||
"devalue": "^5.8.1",
|
|
||||||
"esm-env": "^1.2.2",
|
"esm-env": "^1.2.2",
|
||||||
"kleur": "^4.1.5",
|
"magic-string": "^1.2.3",
|
||||||
"magic-string": "^0.30.5",
|
"mrmime": "^2.0.1",
|
||||||
"mrmime": "^2.0.0",
|
"sirv": "^3.0.2"
|
||||||
"set-cookie-parser": "^3.0.0",
|
|
||||||
"sirv": "^3.0.0"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"svelte-kit": "svelte-kit.js"
|
"svelte-kit": "svelte-kit.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.13"
|
"node": ">=22.17"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.0.0",
|
"@opentelemetry/api": "^1.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"svelte": "^4.0.0 || ^5.0.0-next.0",
|
"svelte": "^5.57.1",
|
||||||
"typescript": "^5.3.3 || ^6.0.0",
|
"typescript": "^6.0.0",
|
||||||
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0"
|
"vite": "^8.0.12"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@opentelemetry/api": {
|
"@opentelemetry/api": {
|
||||||
@@ -1037,6 +1034,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sveltejs/kit/node_modules/magic-string": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sveltejs/load-config": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.3.tgz",
|
||||||
|
"integrity": "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sveltejs/vite-plugin-svelte": {
|
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.3.1.tgz",
|
||||||
@@ -1776,13 +1792,6 @@
|
|||||||
"assertion-error": "^2.0.1"
|
"assertion-error": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/cookie": {
|
|
||||||
"version": "0.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
|
||||||
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/deep-eql": {
|
"node_modules/@types/deep-eql": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||||
@@ -1991,13 +2000,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.6.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
"integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/css.escape": {
|
"node_modules/css.escape": {
|
||||||
@@ -2587,16 +2600,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/kleur": {
|
|
||||||
"version": "4.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
|
||||||
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.33.0",
|
"version": "1.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
||||||
@@ -3188,13 +3191,6 @@
|
|||||||
"node": ">=v12.22.7"
|
"node": ">=v12.22.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/sirv": {
|
"node_modules/sirv": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||||
@@ -3264,13 +3260,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-check": {
|
"node_modules/svelte-check": {
|
||||||
"version": "4.4.8",
|
"version": "4.7.6",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.6.tgz",
|
||||||
"integrity": "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w==",
|
"integrity": "sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "^0.3.25",
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
|
"@sveltejs/load-config": "^0.2.3",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.1",
|
||||||
"fdir": "^6.2.0",
|
"fdir": "^6.2.0",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
@@ -3284,7 +3280,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^4.0.0 || ^5.0.0-next.0",
|
"svelte": "^4.0.0 || ^5.0.0-next.0",
|
||||||
"typescript": ">=5.0.0"
|
"typescript": "^5.0.0 || ^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte/node_modules/@types/estree": {
|
"node_modules/svelte/node_modules/@types/estree": {
|
||||||
@@ -3409,9 +3405,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
+8
-5
@@ -12,7 +12,7 @@
|
|||||||
"tokens": "node scripts/tokens-to-css.js"
|
"tokens": "node scripts/tokens-to-css.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^4.0.0",
|
||||||
"@sveltejs/kit": "^3.0.0",
|
"@sveltejs/kit": "^3.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@tailwindcss/postcss": "^4.3.3",
|
"@tailwindcss/postcss": "^4.3.3",
|
||||||
@@ -21,16 +21,19 @@
|
|||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.1",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^25.0.1",
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.15",
|
||||||
"svelte": "^5.1.9",
|
"svelte": "^5.57.1",
|
||||||
"svelte-check": "^4.4.8",
|
"svelte-check": "^4.7.5",
|
||||||
"tailwindcss": "^4.3.3",
|
"tailwindcss": "^4.3.3",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^6.0.0",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.12",
|
||||||
"vitest": "^5.0.0"
|
"vitest": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@neodrag/svelte": "^2.3.3",
|
"@neodrag/svelte": "^2.3.3",
|
||||||
"@tanstack/svelte-query": "^5.90.2",
|
"@tanstack/svelte-query": "^5.90.2",
|
||||||
"lucide-svelte": "^1.0.1"
|
"lucide-svelte": "^1.0.1"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"#lib/*": "./src/lib/*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ describe('api.get/post/del', () => {
|
|||||||
describe('apiFetch 401 interceptor', () => {
|
describe('apiFetch 401 interceptor', () => {
|
||||||
test('401 response triggers auth.logout({silent:true}) and still throws', async () => {
|
test('401 response triggers auth.logout({silent:true}) and still throws', async () => {
|
||||||
const logoutSpy = vi.fn();
|
const logoutSpy = vi.fn();
|
||||||
vi.doMock('$lib/auth/store.svelte', () => ({
|
vi.doMock('#lib/auth/store.svelte.js', () => ({
|
||||||
logout: logoutSpy,
|
logout: logoutSpy,
|
||||||
login: vi.fn(),
|
login: vi.fn(),
|
||||||
bootstrap: vi.fn(),
|
bootstrap: vi.fn(),
|
||||||
@@ -97,6 +97,6 @@ describe('apiFetch 401 interceptor', () => {
|
|||||||
status: 401
|
status: 401
|
||||||
});
|
});
|
||||||
expect(logoutSpy).toHaveBeenCalledWith({ silent: true });
|
expect(logoutSpy).toHaveBeenCalledWith({ silent: true });
|
||||||
vi.doUnmock('$lib/auth/store.svelte');
|
vi.doUnmock('#lib/auth/store.svelte.js');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export async function apiFetch(path: string, init?: RequestInit): Promise<unknow
|
|||||||
// Lazy import: auth/store imports from this file, so a top-level
|
// Lazy import: auth/store imports from this file, so a top-level
|
||||||
// import would be circular. By the time any 401 actually happens
|
// import would be circular. By the time any 401 actually happens
|
||||||
// at runtime, both modules have finished loading.
|
// at runtime, both modules have finished loading.
|
||||||
const { logout } = await import('$lib/auth/store.svelte');
|
const { logout } = await import('#lib/auth/store.svelte.js');
|
||||||
await logout({ silent: true });
|
await logout({ silent: true });
|
||||||
}
|
}
|
||||||
// Two error envelope shapes ship in this codebase today:
|
// Two error envelope shapes ship in this codebase today:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { api, type User, type LoginResponse } from '$lib/api/client';
|
import { api, type User, type LoginResponse } from '#lib/api/client.js';
|
||||||
import { putMyTimezone } from '$lib/api/me';
|
import { putMyTimezone } from '#lib/api/me.js';
|
||||||
import { queryClient } from '$lib/query/client';
|
import { queryClient } from '#lib/query/client.js';
|
||||||
import { signalSessionEnd } from './sessionEnd.svelte';
|
import { signalSessionEnd } from './sessionEnd.svelte';
|
||||||
import { user, setUser } from './user.svelte';
|
import { user, setUser } from './user.svelte';
|
||||||
|
|
||||||
// Re-export so existing `import { user } from '$lib/auth/store.svelte'`
|
// Re-export so existing `import { user } from '#lib/auth/store.svelte'`
|
||||||
// callers keep working. New code can import directly from auth/user.svelte.
|
// callers keep working. New code can import directly from auth/user.svelte.
|
||||||
export { user };
|
export { user };
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { apiClientMock } from '../../test-utils/mocks/client';
|
import { apiClientMock } from '../../test-utils/mocks/client';
|
||||||
|
|
||||||
vi.mock('$lib/api/client', () => apiClientMock());
|
vi.mock('#lib/api/client.js', () => apiClientMock());
|
||||||
|
|
||||||
vi.mock('$lib/query/client', () => ({
|
vi.mock('#lib/query/client.js', () => ({
|
||||||
queryClient: { clear: vi.fn() }
|
queryClient: { clear: vi.fn() }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { queryClient } from '$lib/query/client';
|
import { queryClient } from '#lib/query/client.js';
|
||||||
import { bootstrap, login, logout, user } from './store.svelte';
|
import { bootstrap, login, logout, user } from './store.svelte';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// component that needs the current user can import `user` from either
|
// component that needs the current user can import `user` from either
|
||||||
// this module or auth/store — auth/store re-exports it for backward
|
// this module or auth/store — auth/store re-exports it for backward
|
||||||
// compatibility.
|
// compatibility.
|
||||||
import type { User } from '$lib/api/client';
|
import type { User } from '#lib/api/client.js';
|
||||||
|
|
||||||
let _user = $state<User | null>(null);
|
let _user = $state<User | null>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
updateAcoustIDSettings,
|
updateAcoustIDSettings,
|
||||||
type AcoustIDOverview,
|
type AcoustIDOverview,
|
||||||
type AcoustIDUnsettled
|
type AcoustIDUnsettled
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// AcoustID recording-id lookup (M401): fills the MusicBrainz ids that
|
// AcoustID recording-id lookup (M401): fills the MusicBrainz ids that
|
||||||
// untagged tracks lack, so the similarity arms can see them. Off until the
|
// untagged tracks lack, so the similarity arms can see them. Off until the
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||||
import { render, screen, fireEvent, waitFor } from "@testing-library/svelte";
|
import { render, screen, fireEvent, waitFor } from "@testing-library/svelte";
|
||||||
import type { AcoustIDOverview } from "$lib/api/admin";
|
import type { AcoustIDOverview } from "#lib/api/admin.js";
|
||||||
|
|
||||||
vi.mock("$lib/api/admin", () => ({
|
vi.mock("#lib/api/admin.js", () => ({
|
||||||
getAcoustID: vi.fn(),
|
getAcoustID: vi.fn(),
|
||||||
updateAcoustIDSettings: vi.fn(),
|
updateAcoustIDSettings: vi.fn(),
|
||||||
runAcoustIDLookup: vi.fn(),
|
runAcoustIDLookup: vi.fn(),
|
||||||
listUnsettledAcoustID: vi.fn(),
|
listUnsettledAcoustID: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("$lib/stores/toast.svelte", () => ({ pushToast: vi.fn() }));
|
vi.mock("#lib/stores/toast.svelte.js", () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import AcoustIDCard from "./AcoustIDCard.svelte";
|
import AcoustIDCard from "./AcoustIDCard.svelte";
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
listUnsettledAcoustID,
|
listUnsettledAcoustID,
|
||||||
runAcoustIDLookup,
|
runAcoustIDLookup,
|
||||||
updateAcoustIDSettings,
|
updateAcoustIDSettings,
|
||||||
} from "$lib/api/admin";
|
} from "#lib/api/admin.js";
|
||||||
|
|
||||||
const base: AcoustIDOverview = {
|
const base: AcoustIDOverview = {
|
||||||
settings: { enabled: true, min_score: 0.85, api_key_set: true },
|
settings: { enabled: true, min_score: 0.85, api_key_set: true },
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
revokeSession,
|
revokeSession,
|
||||||
revokeOtherSessions,
|
revokeOtherSessions,
|
||||||
type ActiveSession
|
type ActiveSession
|
||||||
} from '$lib/api/me';
|
} from '#lib/api/me.js';
|
||||||
import { errCode } from '$lib/api/errors';
|
import { errCode } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// Self-contained: nothing else in the app reads this data, so it holds its
|
// Self-contained: nothing else in the app reads this data, so it holds its
|
||||||
// own state and reloads explicitly rather than joining the query cache.
|
// own state and reloads explicitly rather than joining the query cache.
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ const listSessions = vi.fn();
|
|||||||
const revokeSession = vi.fn();
|
const revokeSession = vi.fn();
|
||||||
const revokeOtherSessions = vi.fn();
|
const revokeOtherSessions = vi.fn();
|
||||||
|
|
||||||
vi.mock('$lib/api/me', () => ({
|
vi.mock('#lib/api/me.js', () => ({
|
||||||
listSessions: (...a: unknown[]) => listSessions(...a),
|
listSessions: (...a: unknown[]) => listSessions(...a),
|
||||||
revokeSession: (...a: unknown[]) => revokeSession(...a),
|
revokeSession: (...a: unknown[]) => revokeSession(...a),
|
||||||
revokeOtherSessions: (...a: unknown[]) => revokeOtherSessions(...a)
|
revokeOtherSessions: (...a: unknown[]) => revokeOtherSessions(...a)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Plus } from 'lucide-svelte';
|
import { Plus } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { createPlaylistsQuery, appendTracks, createPlaylist } from '$lib/api/playlists';
|
import { createPlaylistsQuery, appendTracks, createPlaylist } from '#lib/api/playlists.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import TrackMenuItem from './TrackMenuItem.svelte';
|
import TrackMenuItem from './TrackMenuItem.svelte';
|
||||||
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
tracks,
|
tracks,
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ const playlistsData = vi.hoisted(() => ({
|
|||||||
public: [] as unknown[]
|
public: [] as unknown[]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
|
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => ({
|
vi.mock('#lib/api/playlists.js', () => ({
|
||||||
createPlaylistsQuery: () =>
|
createPlaylistsQuery: () =>
|
||||||
readable({ data: playlistsData, isPending: false, isError: false }),
|
readable({ data: playlistsData, isPending: false, isError: false }),
|
||||||
appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }),
|
appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }),
|
||||||
@@ -86,7 +86,7 @@ describe('AddToPlaylistMenu', () => {
|
|||||||
|
|
||||||
test('clicking a playlist appends and closes', async () => {
|
test('clicking a playlist appends and closes', async () => {
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
const { appendTracks } = await import('$lib/api/playlists');
|
const { appendTracks } = await import('#lib/api/playlists.js');
|
||||||
render(AddToPlaylistMenu, { props: { tracks: [track], onClose } });
|
render(AddToPlaylistMenu, { props: { tracks: [track], onClose } });
|
||||||
await fireEvent.click(screen.getByRole('menuitem', { name: /A-list/ }));
|
await fireEvent.click(screen.getByRole('menuitem', { name: /A-list/ }));
|
||||||
await waitFor(() => expect(onClose).toHaveBeenCalled());
|
await waitFor(() => expect(onClose).toHaveBeenCalled());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AlbumRef, AlbumDetail } from '$lib/api/types';
|
import type { AlbumRef, AlbumDetail } from '#lib/api/types.js';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { enqueueTracks, playQueue } from '$lib/player/store.svelte';
|
import { enqueueTracks, playQueue } from '#lib/player/store.svelte.js';
|
||||||
import { Play } from 'lucide-svelte';
|
import { Play } from 'lucide-svelte';
|
||||||
import AlbumMenu from './AlbumMenu.svelte';
|
import AlbumMenu from './AlbumMenu.svelte';
|
||||||
import CardActionCluster from './CardActionCluster.svelte';
|
import CardActionCluster from './CardActionCluster.svelte';
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import type { AlbumRef, AlbumDetail, TrackRef } from '$lib/api/types';
|
import type { AlbumRef, AlbumDetail, TrackRef } from '#lib/api/types.js';
|
||||||
import { FALLBACK_COVER } from '$lib/media/covers';
|
import { FALLBACK_COVER } from '#lib/media/covers.js';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { apiClientMock } from '../../test-utils/mocks/client';
|
import { apiClientMock } from '../../test-utils/mocks/client';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/api/client', () => apiClientMock());
|
vi.mock('#lib/api/client.js', () => apiClientMock());
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
enqueueTracks: vi.fn(),
|
enqueueTracks: vi.fn(),
|
||||||
playQueue: vi.fn()
|
playQueue: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
vi.mock('$lib/api/albums', async (orig) => {
|
vi.mock('#lib/api/albums.js', async (orig) => {
|
||||||
const actual = (await orig()) as Record<string, unknown>;
|
const actual = (await orig()) as Record<string, unknown>;
|
||||||
return { ...actual, listAlbumTracks: vi.fn().mockResolvedValue([]) };
|
return { ...actual, listAlbumTracks: vi.fn().mockResolvedValue([]) };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', async (orig) => {
|
vi.mock('#lib/api/playlists.js', async (orig) => {
|
||||||
const { readable } = await import('svelte/store');
|
const { readable } = await import('svelte/store');
|
||||||
const actual = (await orig()) as Record<string, unknown>;
|
const actual = (await orig()) as Record<string, unknown>;
|
||||||
return {
|
return {
|
||||||
@@ -34,8 +34,8 @@ vi.mock('$lib/api/playlists', async (orig) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
import AlbumCard from './AlbumCard.svelte';
|
import AlbumCard from './AlbumCard.svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { enqueueTracks, playQueue } from '$lib/player/store.svelte';
|
import { enqueueTracks, playQueue } from '#lib/player/store.svelte.js';
|
||||||
|
|
||||||
const album: AlbumRef = {
|
const album: AlbumRef = {
|
||||||
id: 'xyz',
|
id: 'xyz',
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
import { MoreVertical, Heart, HeartOff, ListMusic, Plus, Disc3 } from 'lucide-svelte';
|
import { MoreVertical, Heart, HeartOff, ListMusic, Plus, Disc3 } from 'lucide-svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '$lib/api/likes';
|
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '#lib/api/likes.js';
|
||||||
import { listAlbumTracks } from '$lib/api/albums';
|
import { listAlbumTracks } from '#lib/api/albums.js';
|
||||||
import { enqueueTracks } from '$lib/player/store.svelte';
|
import { enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
||||||
import TrackMenuItem from './TrackMenuItem.svelte';
|
import TrackMenuItem from './TrackMenuItem.svelte';
|
||||||
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
albumId,
|
albumId,
|
||||||
|
|||||||
@@ -2,28 +2,28 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
|||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTracks } from '$test-utils/fixtures/track';
|
import { makeTracks } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/albums', async (orig) => {
|
vi.mock('#lib/api/albums.js', async (orig) => {
|
||||||
const actual = (await orig()) as Record<string, unknown>;
|
const actual = (await orig()) as Record<string, unknown>;
|
||||||
return { ...actual, listAlbumTracks: vi.fn().mockResolvedValue([]) };
|
return { ...actual, listAlbumTracks: vi.fn().mockResolvedValue([]) };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => emptyPlaylistsMock());
|
vi.mock('#lib/api/playlists.js', () => emptyPlaylistsMock());
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
enqueueTracks: vi.fn()
|
enqueueTracks: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import AlbumMenu from './AlbumMenu.svelte';
|
import AlbumMenu from './AlbumMenu.svelte';
|
||||||
import { enqueueTracks } from '$lib/player/store.svelte';
|
import { enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import { listAlbumTracks } from '$lib/api/albums';
|
import { listAlbumTracks } from '#lib/api/albums.js';
|
||||||
import { likeEntity } from '$lib/api/likes';
|
import { likeEntity } from '#lib/api/likes.js';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
const TRACKS: TrackRef[] = makeTracks(2, {
|
const TRACKS: TrackRef[] = makeTracks(2, {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ApiError } from '$lib/api/client';
|
import type { ApiError } from '#lib/api/client.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ArtistRef, TrackRef } from '$lib/api/types';
|
import type { ArtistRef, TrackRef } from '#lib/api/types.js';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { playQueue, enqueueTracks } from '$lib/player/store.svelte';
|
import { playQueue, enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import { Play } from 'lucide-svelte';
|
import { Play } from 'lucide-svelte';
|
||||||
import ArtistMenu from './ArtistMenu.svelte';
|
import ArtistMenu from './ArtistMenu.svelte';
|
||||||
import CardActionCluster from './CardActionCluster.svelte';
|
import CardActionCluster from './CardActionCluster.svelte';
|
||||||
import Cover from './Cover.svelte';
|
import Cover from './Cover.svelte';
|
||||||
import { listArtistTracks } from '$lib/api/artists';
|
import { listArtistTracks } from '#lib/api/artists.js';
|
||||||
|
|
||||||
let { artist }: { artist: ArtistRef } = $props();
|
let { artist }: { artist: ArtistRef } = $props();
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
|||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { apiClientMock } from '../../test-utils/mocks/client';
|
import { apiClientMock } from '../../test-utils/mocks/client';
|
||||||
import type { ArtistRef, TrackRef } from '$lib/api/types';
|
import type { ArtistRef, TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack, makeTracks } from '$test-utils/fixtures/track';
|
import { makeTrack, makeTracks } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/api/client', () => apiClientMock());
|
vi.mock('#lib/api/client.js', () => apiClientMock());
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playQueue: vi.fn(),
|
playQueue: vi.fn(),
|
||||||
enqueueTracks: vi.fn()
|
enqueueTracks: vi.fn()
|
||||||
}));
|
}));
|
||||||
vi.mock('$lib/api/artists', () => ({
|
vi.mock('#lib/api/artists.js', () => ({
|
||||||
listArtistTracks: vi.fn().mockResolvedValue([])
|
listArtistTracks: vi.fn().mockResolvedValue([])
|
||||||
}));
|
}));
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
import ArtistCard from './ArtistCard.svelte';
|
import ArtistCard from './ArtistCard.svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { playQueue } from '$lib/player/store.svelte';
|
import { playQueue } from '#lib/player/store.svelte.js';
|
||||||
|
|
||||||
const artist: ArtistRef = {
|
const artist: ArtistRef = {
|
||||||
id: 'art-1',
|
id: 'art-1',
|
||||||
@@ -82,7 +82,7 @@ describe('ArtistCard', () => {
|
|||||||
const fakeTracks = [
|
const fakeTracks = [
|
||||||
makeTrack({ artist_id: 'art-1', artist_name: 'BoC' })
|
makeTrack({ artist_id: 'art-1', artist_name: 'BoC' })
|
||||||
];
|
];
|
||||||
const { listArtistTracks } = await import('$lib/api/artists');
|
const { listArtistTracks } = await import('#lib/api/artists.js');
|
||||||
(listArtistTracks as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fakeTracks);
|
(listArtistTracks as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fakeTracks);
|
||||||
render(ArtistCard, { props: { artist } });
|
render(ArtistCard, { props: { artist } });
|
||||||
const btn = screen.getByRole('button', { name: /Add Boards of Canada to queue/i });
|
const btn = screen.getByRole('button', { name: /Add Boards of Canada to queue/i });
|
||||||
@@ -90,7 +90,7 @@ describe('ArtistCard', () => {
|
|||||||
// Allow promise microtask to flush.
|
// Allow promise microtask to flush.
|
||||||
await new Promise((r) => setTimeout(r, 0));
|
await new Promise((r) => setTimeout(r, 0));
|
||||||
expect(listArtistTracks).toHaveBeenCalledWith('art-1');
|
expect(listArtistTracks).toHaveBeenCalledWith('art-1');
|
||||||
const { enqueueTracks } = await import('$lib/player/store.svelte');
|
const { enqueueTracks } = await import('#lib/player/store.svelte.js');
|
||||||
expect(enqueueTracks).toHaveBeenCalledWith(fakeTracks);
|
expect(enqueueTracks).toHaveBeenCalledWith(fakeTracks);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { MoreVertical, Heart, HeartOff, ListMusic, Plus } from 'lucide-svelte';
|
import { MoreVertical, Heart, HeartOff, ListMusic, Plus } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '$lib/api/likes';
|
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '#lib/api/likes.js';
|
||||||
import { listArtistTracks } from '$lib/api/artists';
|
import { listArtistTracks } from '#lib/api/artists.js';
|
||||||
import { enqueueTracks } from '$lib/player/store.svelte';
|
import { enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
||||||
import TrackMenuItem from './TrackMenuItem.svelte';
|
import TrackMenuItem from './TrackMenuItem.svelte';
|
||||||
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
artistId,
|
artistId,
|
||||||
|
|||||||
@@ -2,27 +2,27 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
|||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTracks } from '$test-utils/fixtures/track';
|
import { makeTracks } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/artists', () => ({
|
vi.mock('#lib/api/artists.js', () => ({
|
||||||
listArtistTracks: vi.fn().mockResolvedValue([])
|
listArtistTracks: vi.fn().mockResolvedValue([])
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => emptyPlaylistsMock());
|
vi.mock('#lib/api/playlists.js', () => emptyPlaylistsMock());
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
enqueueTracks: vi.fn()
|
enqueueTracks: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import ArtistMenu from './ArtistMenu.svelte';
|
import ArtistMenu from './ArtistMenu.svelte';
|
||||||
import { enqueueTracks } from '$lib/player/store.svelte';
|
import { enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import { listArtistTracks } from '$lib/api/artists';
|
import { listArtistTracks } from '#lib/api/artists.js';
|
||||||
import { likeEntity, unlikeEntity } from '$lib/api/likes';
|
import { likeEntity, unlikeEntity } from '#lib/api/likes.js';
|
||||||
|
|
||||||
const TRACKS: TrackRef[] = makeTracks(2, {
|
const TRACKS: TrackRef[] = makeTracks(2, {
|
||||||
album_id: 'al1', artist_id: 'a1', artist_name: 'Test'
|
album_id: 'al1', artist_id: 'a1', artist_name: 'Test'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Plus } from 'lucide-svelte';
|
import { Plus } from 'lucide-svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { playQueue, enqueueTrack } from '$lib/player/store.svelte';
|
import { playQueue, enqueueTrack } from '#lib/player/store.svelte.js';
|
||||||
import { coverUrl } from '$lib/media/covers';
|
import { coverUrl } from '#lib/media/covers.js';
|
||||||
import TrackMenu from './TrackMenu.svelte';
|
import TrackMenu from './TrackMenu.svelte';
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
import Cover from './Cover.svelte';
|
import Cover from './Cover.svelte';
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists';
|
||||||
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
||||||
import { makeTracks } from '$test-utils/fixtures/track';
|
import { makeTracks } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
|
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => emptyPlaylistsMock());
|
vi.mock('#lib/api/playlists.js', () => emptyPlaylistsMock());
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { get value() { return { id: 'u1', username: 'u', is_admin: false }; } }
|
user: { get value() { return { id: 'u1', username: 'u', is_admin: false }; } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playQueue: vi.fn(),
|
playQueue: vi.fn(),
|
||||||
enqueueTrack: vi.fn(),
|
enqueueTrack: vi.fn(),
|
||||||
playNext: vi.fn()
|
playNext: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import CompactTrackCard from './CompactTrackCard.svelte';
|
import CompactTrackCard from './CompactTrackCard.svelte';
|
||||||
import { playQueue, enqueueTrack } from '$lib/player/store.svelte';
|
import { playQueue, enqueueTrack } from '#lib/player/store.svelte.js';
|
||||||
|
|
||||||
const titles = ['First', 'Second', 'Third'];
|
const titles = ['First', 'Second', 'Third'];
|
||||||
const tracks: TrackRef[] = makeTracks(3, { artist_name: 'Artist' }).map((t, i) => ({
|
const tracks: TrackRef[] = makeTracks(3, { artist_name: 'Artist' }).map((t, i) => ({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { FALLBACK_COVER } from '$lib/media/covers';
|
import { FALLBACK_COVER } from '#lib/media/covers.js';
|
||||||
import { Disc3 } from 'lucide-svelte';
|
import { Disc3 } from 'lucide-svelte';
|
||||||
|
|
||||||
// Shared cover-artwork renderer. The web counterpart of Android's
|
// Shared cover-artwork renderer. The web counterpart of Android's
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
getFingerprintSettings,
|
getFingerprintSettings,
|
||||||
updateFingerprintSettings,
|
updateFingerprintSettings,
|
||||||
type FingerprintSettings
|
type FingerprintSettings
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// How Minstrel fingerprints tracks and when it looks for duplicates (#3913).
|
// How Minstrel fingerprints tracks and when it looks for duplicates (#3913).
|
||||||
// Lives on the Duplicates page, beside the results these settings shape.
|
// Lives on the Duplicates page, beside the results these settings shape.
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import type { FingerprintSettings } from '$lib/api/admin';
|
import type { FingerprintSettings } from '#lib/api/admin.js';
|
||||||
import { ERROR_COPY } from '$lib/api/error-copy';
|
import { ERROR_COPY } from '#lib/api/error-copy.js';
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getFingerprintSettings: vi.fn(),
|
getFingerprintSettings: vi.fn(),
|
||||||
updateFingerprintSettings: vi.fn()
|
updateFingerprintSettings: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import FingerprintSettingsCard from './FingerprintSettingsCard.svelte';
|
import FingerprintSettingsCard from './FingerprintSettingsCard.svelte';
|
||||||
import { getFingerprintSettings, updateFingerprintSettings } from '$lib/api/admin';
|
import { getFingerprintSettings, updateFingerprintSettings } from '#lib/api/admin.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
const base: FingerprintSettings = {
|
const base: FingerprintSettings = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
import { Flag } from 'lucide-svelte';
|
import { Flag } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { flagTrack } from '$lib/api/quarantine';
|
import { flagTrack } from '#lib/api/quarantine.js';
|
||||||
import { errCode } from '$lib/api/errors';
|
import { errCode } from '#lib/api/errors.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import type { TrackRef, LidarrQuarantineReason } from '$lib/api/types';
|
import type { TrackRef, LidarrQuarantineReason } from '#lib/api/types.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
track,
|
track,
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => ({
|
vi.mock('#lib/api/quarantine.js', () => ({
|
||||||
flagTrack: vi.fn().mockResolvedValue({ track_id: 't1', reason: 'bad_rip' })
|
flagTrack: vi.fn().mockResolvedValue({ track_id: 't1', reason: 'bad_rip' })
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { flagTrack } from '$lib/api/quarantine';
|
import { flagTrack } from '#lib/api/quarantine.js';
|
||||||
|
|
||||||
const track = makeTrack();
|
const track = makeTrack();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { HistoryEvent } from '$lib/api/history';
|
import type { HistoryEvent } from '#lib/api/history.js';
|
||||||
import { playQueue } from '$lib/player/store.svelte';
|
import { playQueue } from '#lib/player/store.svelte.js';
|
||||||
import { coverUrl } from '$lib/media/covers';
|
import { coverUrl } from '#lib/media/covers.js';
|
||||||
|
|
||||||
let { event } = $props<{ event: HistoryEvent }>();
|
let { event } = $props<{ event: HistoryEvent }>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import HistoryRow from './HistoryRow.svelte';
|
import HistoryRow from './HistoryRow.svelte';
|
||||||
import type { HistoryEvent } from '$lib/api/history';
|
import type { HistoryEvent } from '#lib/api/history.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
const playQueue = vi.fn();
|
const playQueue = vi.fn();
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playQueue: (...args: unknown[]) => playQueue(...args)
|
playQueue: (...args: unknown[]) => playQueue(...args)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { createLikedIdsQuery, likeEntity, unlikeEntity, type EntityKind } from '$lib/api/likes';
|
import { createLikedIdsQuery, likeEntity, unlikeEntity, type EntityKind } from '#lib/api/likes.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
entityType,
|
entityType,
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ const cacheState = vi.hoisted(() => ({
|
|||||||
data: { track_ids: ['t1'] as string[], album_ids: [] as string[], artist_ids: [] as string[] }
|
data: { track_ids: ['t1'] as string[], album_ids: [] as string[], artist_ids: [] as string[] }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => ({
|
vi.mock('#lib/api/likes.js', () => ({
|
||||||
createLikedIdsQuery: () => readable({ data: cacheState.data, isPending: false, isError: false }),
|
createLikedIdsQuery: () => readable({ data: cacheState.data, isPending: false, isError: false }),
|
||||||
likeEntity: vi.fn().mockResolvedValue(undefined),
|
likeEntity: vi.fn().mockResolvedValue(undefined),
|
||||||
unlikeEntity: vi.fn().mockResolvedValue(undefined)
|
unlikeEntity: vi.fn().mockResolvedValue(undefined)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
import { likeEntity, unlikeEntity } from '$lib/api/likes';
|
import { likeEntity, unlikeEntity } from '#lib/api/likes.js';
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
updateLoudnessSettings,
|
updateLoudnessSettings,
|
||||||
type LoudnessCoverage,
|
type LoudnessCoverage,
|
||||||
type LoudnessSettings
|
type LoudnessSettings
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// Loudness analysis (#4995): the background measurement that loudness
|
// Loudness analysis (#4995): the background measurement that loudness
|
||||||
// normalization levels playback from, its progress, and its knobs. The
|
// normalization levels playback from, its progress, and its knobs. The
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import type { LoudnessCoverage, LoudnessSettings } from '$lib/api/admin';
|
import type { LoudnessCoverage, LoudnessSettings } from '#lib/api/admin.js';
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getLoudnessSettings: vi.fn(),
|
getLoudnessSettings: vi.fn(),
|
||||||
updateLoudnessSettings: vi.fn(),
|
updateLoudnessSettings: vi.fn(),
|
||||||
getLoudnessCoverage: vi.fn()
|
getLoudnessCoverage: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import LoudnessSettingsCard from './LoudnessSettingsCard.svelte';
|
import LoudnessSettingsCard from './LoudnessSettingsCard.svelte';
|
||||||
import { getLoudnessCoverage, getLoudnessSettings, updateLoudnessSettings } from '$lib/api/admin';
|
import { getLoudnessCoverage, getLoudnessSettings, updateLoudnessSettings } from '#lib/api/admin.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
const base: LoudnessSettings = { enabled: true, backfill_concurrency: 2, leveled_cache_mb: 2048 };
|
const base: LoudnessSettings = { enabled: true, backfill_concurrency: 2, leveled_cache_mb: 2048 };
|
||||||
const coverage: LoudnessCoverage = {
|
const coverage: LoudnessCoverage = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { Smartphone } from 'lucide-svelte';
|
import { Smartphone } from 'lucide-svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
|
|
||||||
// Polls /api/client/version once on mount. Renders nothing on 404
|
// Polls /api/client/version once on mount. Renders nothing on 404
|
||||||
// (no bundled APK — graceful degradation in dev / pre-CI images)
|
// (no bundled APK — graceful degradation in dev / pre-CI images)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
getNetworkSettings,
|
getNetworkSettings,
|
||||||
updateNetworkSettings,
|
updateNetworkSettings,
|
||||||
type NetworkSettings
|
type NetworkSettings
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
let settings = $state<NetworkSettings | null>(null);
|
let settings = $state<NetworkSettings | null>(null);
|
||||||
let hops = $state(1);
|
let hops = $state(1);
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import NetworkSettingsCard from './NetworkSettingsCard.svelte';
|
|||||||
const getNetworkSettings = vi.fn();
|
const getNetworkSettings = vi.fn();
|
||||||
const updateNetworkSettings = vi.fn();
|
const updateNetworkSettings = vi.fn();
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getNetworkSettings: () => getNetworkSettings(),
|
getNetworkSettings: () => getNetworkSettings(),
|
||||||
updateNetworkSettings: (hops: number) => updateNetworkSettings(hops)
|
updateNetworkSettings: (hops: number) => updateNetworkSettings(hops)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
function settings(over: Record<string, unknown> = {}) {
|
function settings(over: Record<string, unknown> = {}) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
NORMALIZATION_MODES,
|
NORMALIZATION_MODES,
|
||||||
NORMALIZATION_TARGETS,
|
NORMALIZATION_TARGETS,
|
||||||
type NormalizationPrefs
|
type NormalizationPrefs
|
||||||
} from '$lib/api/normalization';
|
} from '#lib/api/normalization.js';
|
||||||
import {
|
import {
|
||||||
normalization,
|
normalization,
|
||||||
loadNormalization,
|
loadNormalization,
|
||||||
setNormalization
|
setNormalization
|
||||||
} from '$lib/stores/normalization.svelte';
|
} from '#lib/stores/normalization.svelte.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// Volume leveling (M464 #4998). The choice is the user's, stored on the
|
// Volume leveling (M464 #4998). The choice is the user's, stored on the
|
||||||
// server, so it follows them to the Android app and to casts.
|
// server, so it follows them to the Android app and to casts.
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
|
|
||||||
vi.mock('$lib/api/normalization', async (importOriginal) => ({
|
vi.mock('#lib/api/normalization.js', async (importOriginal) => ({
|
||||||
...(await importOriginal<typeof import('$lib/api/normalization')>()),
|
...(await importOriginal<typeof import('#lib/api/normalization.js')>()),
|
||||||
getNormalization: vi.fn(),
|
getNormalization: vi.fn(),
|
||||||
putNormalization: vi.fn()
|
putNormalization: vi.fn()
|
||||||
}));
|
}));
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import NormalizationSetting from './NormalizationSetting.svelte';
|
import NormalizationSetting from './NormalizationSetting.svelte';
|
||||||
import { getNormalization, putNormalization } from '$lib/api/normalization';
|
import { getNormalization, putNormalization } from '#lib/api/normalization.js';
|
||||||
import { normalization } from '$lib/stores/normalization.svelte';
|
import { normalization } from '#lib/stores/normalization.svelte.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
markAllNotificationsRead,
|
markAllNotificationsRead,
|
||||||
markNotificationRead,
|
markNotificationRead,
|
||||||
type NotificationItem
|
type NotificationItem
|
||||||
} from '$lib/api/notifications';
|
} from '#lib/api/notifications.js';
|
||||||
import { relativeTime } from '$lib/utils/relativeTime';
|
import { relativeTime } from '#lib/utils/relativeTime.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// The notifications inbox (M489): a bell in the header with an unread badge,
|
// The notifications inbox (M489): a bell in the header with an unread badge,
|
||||||
// opening a panel of what happened while you were away.
|
// opening a panel of what happened while you were away.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
|
|||||||
return { ...actual, useQueryClient: () => ({ invalidateQueries: invalidateMock }) };
|
return { ...actual, useQueryClient: () => ({ invalidateQueries: invalidateMock }) };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/notifications', async (orig) => {
|
vi.mock('#lib/api/notifications.js', async (orig) => {
|
||||||
const actual = (await orig()) as Record<string, unknown>;
|
const actual = (await orig()) as Record<string, unknown>;
|
||||||
return {
|
return {
|
||||||
badgeLabel: actual.badgeLabel,
|
badgeLabel: actual.badgeLabel,
|
||||||
@@ -19,7 +19,7 @@ vi.mock('$lib/api/notifications', async (orig) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import NotificationBell from './NotificationBell.svelte';
|
import NotificationBell from './NotificationBell.svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
markAllNotificationsRead,
|
markAllNotificationsRead,
|
||||||
markNotificationRead,
|
markNotificationRead,
|
||||||
type NotificationItem
|
type NotificationItem
|
||||||
} from '$lib/api/notifications';
|
} from '#lib/api/notifications.js';
|
||||||
|
|
||||||
function item(over: Partial<NotificationItem> = {}): NotificationItem {
|
function item(over: Partial<NotificationItem> = {}): NotificationItem {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
getNotificationEmailSettings,
|
getNotificationEmailSettings,
|
||||||
updateNotificationEmailSettings,
|
updateNotificationEmailSettings,
|
||||||
type NotificationEmailSettings
|
type NotificationEmailSettings
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// When notification emails go out (M489 #5346). Nothing is emailed per
|
// When notification emails go out (M489 #5346). Nothing is emailed per
|
||||||
// event: new music is a daily summary, everything else a batch.
|
// event: new music is a daily summary, everything else a batch.
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getNotificationEmailSettings: vi.fn(),
|
getNotificationEmailSettings: vi.fn(),
|
||||||
updateNotificationEmailSettings: vi.fn()
|
updateNotificationEmailSettings: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import NotificationEmailCard from './NotificationEmailCard.svelte';
|
import NotificationEmailCard from './NotificationEmailCard.svelte';
|
||||||
import { getNotificationEmailSettings, updateNotificationEmailSettings } from '$lib/api/admin';
|
import { getNotificationEmailSettings, updateNotificationEmailSettings } from '#lib/api/admin.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
afterEach(() => vi.clearAllMocks());
|
afterEach(() => vi.clearAllMocks());
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
type NotificationKindSetting,
|
type NotificationKindSetting,
|
||||||
type NotificationSettingChange,
|
type NotificationSettingChange,
|
||||||
type NotificationSettings
|
type NotificationSettings
|
||||||
} from '$lib/api/notifications';
|
} from '#lib/api/notifications.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { user } from '$lib/auth/store.svelte';
|
import { user } from '#lib/auth/store.svelte.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// Per-user notification settings (M489): one row per kind, a toggle per
|
// Per-user notification settings (M489): one row per kind, a toggle per
|
||||||
// channel. Toggles are optimistic; a failed save puts the switch back.
|
// channel. Toggles are optimistic; a failed save puts the switch back.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
|
|||||||
return { ...actual, useQueryClient: () => ({ setQueryData, invalidateQueries: vi.fn() }) };
|
return { ...actual, useQueryClient: () => ({ setQueryData, invalidateQueries: vi.fn() }) };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/notifications', async (orig) => {
|
vi.mock('#lib/api/notifications.js', async (orig) => {
|
||||||
const actual = (await orig()) as Record<string, unknown>;
|
const actual = (await orig()) as Record<string, unknown>;
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
@@ -18,12 +18,12 @@ vi.mock('$lib/api/notifications', async (orig) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const userState = vi.hoisted(() => ({ current: { id: '1', username: 'alice', is_admin: false } }));
|
const userState = vi.hoisted(() => ({ current: { id: '1', username: 'alice', is_admin: false } }));
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { get value() { return userState.current; } }
|
user: { get value() { return userState.current; } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const pushToast = vi.fn();
|
const pushToast = vi.fn();
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: (...a: unknown[]) => pushToast(...a) }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: (...a: unknown[]) => pushToast(...a) }));
|
||||||
|
|
||||||
import NotificationSettings from './NotificationSettings.svelte';
|
import NotificationSettings from './NotificationSettings.svelte';
|
||||||
import {
|
import {
|
||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
putNotificationSettings,
|
putNotificationSettings,
|
||||||
type NotificationKindSetting,
|
type NotificationKindSetting,
|
||||||
type NotificationSettings as Settings
|
type NotificationSettings as Settings
|
||||||
} from '$lib/api/notifications';
|
} from '#lib/api/notifications.js';
|
||||||
|
|
||||||
const requesterKinds: NotificationKindSetting[] = [
|
const requesterKinds: NotificationKindSetting[] = [
|
||||||
{ kind: 'request_approved', admin_only: false, inbox: true, phone: true, email: true },
|
{ kind: 'request_approved', admin_only: false, inbox: true, phone: true, email: true },
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
togglePlay, skipNext, skipPrev, seekTo, setVolume,
|
togglePlay, skipNext, skipPrev, seekTo, setVolume,
|
||||||
toggleShuffle, cycleRepeat, playQueue,
|
toggleShuffle, cycleRepeat, playQueue,
|
||||||
toggleQueueDrawer
|
toggleQueueDrawer
|
||||||
} from '$lib/player/store.svelte';
|
} from '#lib/player/store.svelte.js';
|
||||||
import { formatDuration } from '$lib/media/duration';
|
import { formatDuration } from '#lib/media/duration.js';
|
||||||
import { FALLBACK_COVER, coverUrl } from '$lib/media/covers';
|
import { FALLBACK_COVER, coverUrl } from '#lib/media/covers.js';
|
||||||
import { dominantColorFromUrl, rgbToCssString } from '$lib/media/dominantColor';
|
import { dominantColorFromUrl, rgbToCssString } from '#lib/media/dominantColor.js';
|
||||||
import { useSmoothPosition } from '$lib/player/smoothPosition.svelte';
|
import { useSmoothPosition } from '#lib/player/smoothPosition.svelte.js';
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
import TrackMenu from './TrackMenu.svelte';
|
import TrackMenu from './TrackMenu.svelte';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, within } from '@testing-library/svelte';
|
import { render, screen, fireEvent, within } from '@testing-library/svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
@@ -20,7 +20,7 @@ const state = vi.hoisted(() => ({
|
|||||||
queueDrawerOpen: false
|
queueDrawerOpen: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
player: {
|
player: {
|
||||||
get queue() { return state.queue; },
|
get queue() { return state.queue; },
|
||||||
get index() { return state.index; },
|
get index() { return state.index; },
|
||||||
@@ -46,16 +46,16 @@ vi.mock('$lib/player/store.svelte', () => ({
|
|||||||
toggleQueueDrawer: vi.fn()
|
toggleQueueDrawer: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
|
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
|
||||||
|
|
||||||
import PlayerBar from './PlayerBar.svelte';
|
import PlayerBar from './PlayerBar.svelte';
|
||||||
import {
|
import {
|
||||||
togglePlay, skipNext, skipPrev, seekTo, setVolume,
|
togglePlay, skipNext, skipPrev, seekTo, setVolume,
|
||||||
toggleShuffle, cycleRepeat, playQueue,
|
toggleShuffle, cycleRepeat, playQueue,
|
||||||
toggleQueueDrawer
|
toggleQueueDrawer
|
||||||
} from '$lib/player/store.svelte';
|
} from '#lib/player/store.svelte.js';
|
||||||
|
|
||||||
function track(): TrackRef {
|
function track(): TrackRef {
|
||||||
return makeTrack({
|
return makeTrack({
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Play, MoreVertical, RefreshCcw } from 'lucide-svelte';
|
import { Play, MoreVertical, RefreshCcw } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import type { Playlist, PlaylistTrack, TrackRef } from '$lib/api/types';
|
import type { Playlist, PlaylistTrack, TrackRef } from '#lib/api/types.js';
|
||||||
import { user } from '$lib/auth/store.svelte';
|
import { user } from '#lib/auth/store.svelte.js';
|
||||||
import { getPlaylist, systemShuffle, refreshSystem } from '$lib/api/playlists';
|
import { getPlaylist, systemShuffle, refreshSystem } from '#lib/api/playlists.js';
|
||||||
import { playlistTrackToRef } from '$lib/playlists/playlistTrackToRef';
|
import { playlistTrackToRef } from '#lib/playlists/playlistTrackToRef.js';
|
||||||
import { systemPlaylistRefetch } from '$lib/playlists/systemRefetch';
|
import { systemPlaylistRefetch } from '#lib/playlists/systemRefetch.js';
|
||||||
import { errCode } from '$lib/api/errors';
|
import { errCode } from '#lib/api/errors.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { playQueue } from '$lib/player/store.svelte';
|
import { playQueue } from '#lib/player/store.svelte.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
let { playlist }: { playlist: Playlist } = $props();
|
let { playlist }: { playlist: Playlist } = $props();
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import PlaylistCard from './PlaylistCard.svelte';
|
import PlaylistCard from './PlaylistCard.svelte';
|
||||||
import type { Playlist, PlaylistDetail } from '$lib/api/types';
|
import type { Playlist, PlaylistDetail } from '#lib/api/types.js';
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
|
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => ({
|
vi.mock('#lib/api/playlists.js', () => ({
|
||||||
getPlaylist: vi.fn().mockResolvedValue({
|
getPlaylist: vi.fn().mockResolvedValue({
|
||||||
id: 'p-1',
|
id: 'p-1',
|
||||||
user_id: 'u-self',
|
user_id: 'u-self',
|
||||||
@@ -75,11 +75,11 @@ vi.mock('$lib/api/playlists', () => ({
|
|||||||
refreshSystem: vi.fn().mockResolvedValue({ playlist_id: 'p-1', track_count: 5, track_ids: ['t-1'] })
|
refreshSystem: vi.fn().mockResolvedValue({ playlist_id: 'p-1', track_count: 5, track_ids: ['t-1'] })
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playQueue: vi.fn()
|
playQueue: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/queries', () => ({
|
vi.mock('#lib/api/queries.js', () => ({
|
||||||
qk: {
|
qk: {
|
||||||
playlist: (id: string) => ['playlist', id],
|
playlist: (id: string) => ['playlist', id],
|
||||||
playlists: (kind?: string) => ['playlists', { kind: kind ?? 'user' }]
|
playlists: (kind?: string) => ['playlists', { kind: kind ?? 'user' }]
|
||||||
@@ -162,8 +162,8 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('play button calls playQueue with tracks when clicked', async () => {
|
test('play button calls playQueue with tracks when clicked', async () => {
|
||||||
const { getPlaylist } = await import('$lib/api/playlists');
|
const { getPlaylist } = await import('#lib/api/playlists.js');
|
||||||
const { playQueue } = await import('$lib/player/store.svelte');
|
const { playQueue } = await import('#lib/player/store.svelte.js');
|
||||||
|
|
||||||
render(PlaylistCard, { props: { playlist: base } });
|
render(PlaylistCard, { props: { playlist: base } });
|
||||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||||
@@ -222,7 +222,7 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('clicking Refresh calls refreshSystem with the raw variant', async () => {
|
test('clicking Refresh calls refreshSystem with the raw variant', async () => {
|
||||||
const { refreshSystem } = await import('$lib/api/playlists');
|
const { refreshSystem } = await import('#lib/api/playlists.js');
|
||||||
const discoverPlaylist: Playlist = {
|
const discoverPlaylist: Playlist = {
|
||||||
...base,
|
...base,
|
||||||
kind: 'system',
|
kind: 'system',
|
||||||
@@ -237,8 +237,8 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('For-You play calls systemShuffle, not getPlaylist', async () => {
|
test('For-You play calls systemShuffle, not getPlaylist', async () => {
|
||||||
const { getPlaylist, systemShuffle } = await import('$lib/api/playlists');
|
const { getPlaylist, systemShuffle } = await import('#lib/api/playlists.js');
|
||||||
const { playQueue } = await import('$lib/player/store.svelte');
|
const { playQueue } = await import('#lib/player/store.svelte.js');
|
||||||
const forYouPlaylist: Playlist = {
|
const forYouPlaylist: Playlist = {
|
||||||
...base,
|
...base,
|
||||||
kind: 'system',
|
kind: 'system',
|
||||||
@@ -261,7 +261,7 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('For-You play passes source:for_you to playQueue (no client shuffle)', async () => {
|
test('For-You play passes source:for_you to playQueue (no client shuffle)', async () => {
|
||||||
const { playQueue } = await import('$lib/player/store.svelte');
|
const { playQueue } = await import('#lib/player/store.svelte.js');
|
||||||
const forYouPlaylist: Playlist = {
|
const forYouPlaylist: Playlist = {
|
||||||
...base,
|
...base,
|
||||||
kind: 'system',
|
kind: 'system',
|
||||||
@@ -281,8 +281,8 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('user-playlist play uses getPlaylist + plain playQueue (no source)', async () => {
|
test('user-playlist play uses getPlaylist + plain playQueue (no source)', async () => {
|
||||||
const { getPlaylist, systemShuffle } = await import('$lib/api/playlists');
|
const { getPlaylist, systemShuffle } = await import('#lib/api/playlists.js');
|
||||||
const { playQueue } = await import('$lib/player/store.svelte');
|
const { playQueue } = await import('#lib/player/store.svelte.js');
|
||||||
render(PlaylistCard, { props: { playlist: base } });
|
render(PlaylistCard, { props: { playlist: base } });
|
||||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||||
await fireEvent.click(playButton);
|
await fireEvent.click(playButton);
|
||||||
@@ -294,8 +294,8 @@ describe('PlaylistCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('user-playlist play does not refresh anything', async () => {
|
test('user-playlist play does not refresh anything', async () => {
|
||||||
const { refreshSystem, getPlaylist } = await import('$lib/api/playlists');
|
const { refreshSystem, getPlaylist } = await import('#lib/api/playlists.js');
|
||||||
const { playQueue } = await import('$lib/player/store.svelte');
|
const { playQueue } = await import('#lib/player/store.svelte.js');
|
||||||
|
|
||||||
render(PlaylistCard, { props: { playlist: base } });
|
render(PlaylistCard, { props: { playlist: base } });
|
||||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
import { draggable, type DragEventData } from '@neodrag/svelte';
|
import { draggable, type DragEventData } from '@neodrag/svelte';
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
import TrackMenu from './TrackMenu.svelte';
|
import TrackMenu from './TrackMenu.svelte';
|
||||||
import type { PlaylistTrack } from '$lib/api/types';
|
import type { PlaylistTrack } from '#lib/api/types.js';
|
||||||
import { playlistTrackToRef } from '$lib/playlists/playlistTrackToRef';
|
import { playlistTrackToRef } from '#lib/playlists/playlistTrackToRef.js';
|
||||||
import { player } from '$lib/player/store.svelte';
|
import { player } from '#lib/player/store.svelte.js';
|
||||||
import { offsetToDelta } from './queue-row-math';
|
import { offsetToDelta } from './queue-row-math';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
|||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
||||||
import type { PlaylistTrack } from '$lib/api/types';
|
import type { PlaylistTrack } from '#lib/api/types.js';
|
||||||
|
|
||||||
// LikeButton + TrackMenu transitively pull in TanStack Query, the auth
|
// LikeButton + TrackMenu transitively pull in TanStack Query, the auth
|
||||||
// store, likes/quarantine APIs, the player store, and SvelteKit
|
// store, likes/quarantine APIs, the player store, and SvelteKit
|
||||||
// navigation. Mock all of them at module level so the component can be
|
// navigation. Mock all of them at module level so the component can be
|
||||||
// rendered in isolation.
|
// rendered in isolation.
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { value: { id: 'u1', username: 'me', is_admin: false } }
|
user: { value: { id: 'u1', username: 'me', is_admin: false } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -17,11 +17,11 @@ vi.mock('$app/navigation', () => ({
|
|||||||
goto: vi.fn()
|
goto: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
|
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playNext: vi.fn(),
|
playNext: vi.fn(),
|
||||||
enqueueTrack: vi.fn(),
|
enqueueTrack: vi.fn(),
|
||||||
playQueue: vi.fn(),
|
playQueue: vi.fn(),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { Save, TriangleAlert } from 'lucide-svelte';
|
import { Save, TriangleAlert } from 'lucide-svelte';
|
||||||
import { getNetworkSettings, updatePublicUrl } from '$lib/api/admin';
|
import { getNetworkSettings, updatePublicUrl } from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
let saved = $state<string | null>(null);
|
let saved = $state<string | null>(null);
|
||||||
let value = $state('');
|
let value = $state('');
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import PublicAddressCard from './PublicAddressCard.svelte';
|
|||||||
const getNetworkSettings = vi.fn();
|
const getNetworkSettings = vi.fn();
|
||||||
const updatePublicUrl = vi.fn();
|
const updatePublicUrl = vi.fn();
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getNetworkSettings: () => getNetworkSettings(),
|
getNetworkSettings: () => getNetworkSettings(),
|
||||||
updatePublicUrl: (url: string) => updatePublicUrl(url)
|
updatePublicUrl: (url: string) => updatePublicUrl(url)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const pushToast = vi.fn();
|
const pushToast = vi.fn();
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({
|
vi.mock('#lib/stores/toast.svelte.js', () => ({
|
||||||
pushToast: (...args: unknown[]) => pushToast(...args)
|
pushToast: (...args: unknown[]) => pushToast(...args)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { player, closeQueueDrawer } from '$lib/player/store.svelte';
|
import { player, closeQueueDrawer } from '#lib/player/store.svelte.js';
|
||||||
import QueueList from './QueueList.svelte';
|
import QueueList from './QueueList.svelte';
|
||||||
|
|
||||||
let previouslyFocused: HTMLElement | null = null;
|
let previouslyFocused: HTMLElement | null = null;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
// QueueTrackRow (rendered inside QueueDrawer → QueueList) now renders a
|
// QueueTrackRow (rendered inside QueueDrawer → QueueList) now renders a
|
||||||
@@ -10,14 +10,14 @@ import { makeTrack } from '$test-utils/fixtures/track';
|
|||||||
// emptyLikesMock import — must precede the QueueDrawer import below, since
|
// emptyLikesMock import — must precede the QueueDrawer import below, since
|
||||||
// loading the component evaluates the mocked modules and the hoisted
|
// loading the component evaluates the mocked modules and the hoisted
|
||||||
// factories need their referenced bindings already initialized.
|
// factories need their referenced bindings already initialized.
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
const closeQueueDrawer = vi.fn();
|
const closeQueueDrawer = vi.fn();
|
||||||
let queueValue: TrackRef[] = [];
|
let queueValue: TrackRef[] = [];
|
||||||
let indexValue = 0;
|
let indexValue = 0;
|
||||||
let openValue = true;
|
let openValue = true;
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
closeQueueDrawer: (...args: unknown[]) => closeQueueDrawer(...args),
|
closeQueueDrawer: (...args: unknown[]) => closeQueueDrawer(...args),
|
||||||
player: {
|
player: {
|
||||||
get queue() { return queueValue; },
|
get queue() { return queueValue; },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
import { X, Trash2, ArrowDown } from 'lucide-svelte';
|
import { X, Trash2, ArrowDown } from 'lucide-svelte';
|
||||||
import { player, clearQueue } from '$lib/player/store.svelte';
|
import { player, clearQueue } from '#lib/player/store.svelte.js';
|
||||||
import QueueTrackRow from './QueueTrackRow.svelte';
|
import QueueTrackRow from './QueueTrackRow.svelte';
|
||||||
|
|
||||||
// onClose: when provided, renders an X button in the header so the
|
// onClose: when provided, renders an X button in the header so the
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { GripVertical, X } from 'lucide-svelte';
|
import { GripVertical, X } from 'lucide-svelte';
|
||||||
import { draggable, type DragEventData } from '@neodrag/svelte';
|
import { draggable, type DragEventData } from '@neodrag/svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { playFromQueueIndex, removeFromQueue, moveQueueItem } from '$lib/player/store.svelte';
|
import { playFromQueueIndex, removeFromQueue, moveQueueItem } from '#lib/player/store.svelte.js';
|
||||||
import { coverUrl, FALLBACK_COVER } from '$lib/media/covers';
|
import { coverUrl, FALLBACK_COVER } from '#lib/media/covers.js';
|
||||||
import { offsetToDelta } from './queue-row-math';
|
import { offsetToDelta } from './queue-row-math';
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { makeTrack } from '$test-utils/fixtures/track';
|
|||||||
// The mock (and its emptyLikesMock import) must precede the component import
|
// The mock (and its emptyLikesMock import) must precede the component import
|
||||||
// below: importing QueueTrackRow transitively loads LikeButton → the mocked
|
// below: importing QueueTrackRow transitively loads LikeButton → the mocked
|
||||||
// module, and the hoisted factory needs emptyLikesMock already initialized.
|
// module, and the hoisted factory needs emptyLikesMock already initialized.
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
import QueueTrackRow from './QueueTrackRow.svelte';
|
import QueueTrackRow from './QueueTrackRow.svelte';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ const playFromQueueIndex = vi.fn();
|
|||||||
const removeFromQueue = vi.fn();
|
const removeFromQueue = vi.fn();
|
||||||
const moveQueueItem = vi.fn();
|
const moveQueueItem = vi.fn();
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playFromQueueIndex: (...args: unknown[]) => playFromQueueIndex(...args),
|
playFromQueueIndex: (...args: unknown[]) => playFromQueueIndex(...args),
|
||||||
removeFromQueue: (...args: unknown[]) => removeFromQueue(...args),
|
removeFromQueue: (...args: unknown[]) => removeFromQueue(...args),
|
||||||
moveQueueItem: (...args: unknown[]) => moveQueueItem(...args)
|
moveQueueItem: (...args: unknown[]) => moveQueueItem(...args)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
getReacquisitionSettings,
|
getReacquisitionSettings,
|
||||||
updateReacquisitionSettings,
|
updateReacquisitionSettings,
|
||||||
type ReacquisitionSettings
|
type ReacquisitionSettings
|
||||||
} from '$lib/api/admin';
|
} from '#lib/api/admin.js';
|
||||||
import { errMessage } from '$lib/api/errors';
|
import { errMessage } from '#lib/api/errors.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
// Policy for turning a missing file back into a Lidarr request
|
// Policy for turning a missing file back into a Lidarr request
|
||||||
// (milestone #290). Lives on the missing-files page rather than under
|
// (milestone #290). Lives on the missing-files page rather than under
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||||
import type { ReacquisitionSettings } from '$lib/api/admin';
|
import type { ReacquisitionSettings } from '#lib/api/admin.js';
|
||||||
import { ERROR_COPY } from '$lib/api/error-copy';
|
import { ERROR_COPY } from '#lib/api/error-copy.js';
|
||||||
|
|
||||||
vi.mock('$lib/api/admin', () => ({
|
vi.mock('#lib/api/admin.js', () => ({
|
||||||
getReacquisitionSettings: vi.fn(),
|
getReacquisitionSettings: vi.fn(),
|
||||||
updateReacquisitionSettings: vi.fn()
|
updateReacquisitionSettings: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: vi.fn() }));
|
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||||
|
|
||||||
import ReacquisitionSettingsCard from './ReacquisitionSettingsCard.svelte';
|
import ReacquisitionSettingsCard from './ReacquisitionSettingsCard.svelte';
|
||||||
import { getReacquisitionSettings, updateReacquisitionSettings } from '$lib/api/admin';
|
import { getReacquisitionSettings, updateReacquisitionSettings } from '#lib/api/admin.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
|
|
||||||
const base: ReacquisitionSettings = {
|
const base: ReacquisitionSettings = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -18,16 +18,16 @@
|
|||||||
function navigate(next: string) {
|
function navigate(next: string) {
|
||||||
const onSearchPage = page.url.pathname.startsWith('/search');
|
const onSearchPage = page.url.pathname.startsWith('/search');
|
||||||
const trimmed = next.trim();
|
const trimmed = next.trim();
|
||||||
// keepFocus: SvelteKit blurs the active element on navigation by default.
|
// reset: false: SvelteKit blurs the active element and scrolls to the top
|
||||||
// For a typeahead-style URL update we want the user to keep typing
|
// on navigation by default. For a typeahead-style URL update we want the
|
||||||
// without re-clicking the input every 250 ms.
|
// user to keep typing without re-clicking the input every 250 ms.
|
||||||
if (trimmed === '') {
|
if (trimmed === '') {
|
||||||
if (onSearchPage) goto('/search', { replaceState: true, keepFocus: true });
|
if (onSearchPage) goto('/search', { replace: true, reset: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
goto(`/search?q=${encodeURIComponent(trimmed)}`, {
|
goto(`/search?q=${encodeURIComponent(trimmed)}`, {
|
||||||
replaceState: onSearchPage,
|
replace: onSearchPage,
|
||||||
keepFocus: true,
|
reset: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ describe('SearchInput', () => {
|
|||||||
expect(goto).not.toHaveBeenCalled();
|
expect(goto).not.toHaveBeenCalled();
|
||||||
vi.advanceTimersByTime(250);
|
vi.advanceTimersByTime(250);
|
||||||
expect(goto).toHaveBeenCalledTimes(1);
|
expect(goto).toHaveBeenCalledTimes(1);
|
||||||
expect(goto).toHaveBeenCalledWith('/search?q=h', { replaceState: false, keepFocus: true });
|
expect(goto).toHaveBeenCalledWith('/search?q=h', { replace: false, reset: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rapid keystrokes debounce to a single goto', async () => {
|
test('rapid keystrokes debounce to a single goto', async () => {
|
||||||
@@ -53,16 +53,16 @@ describe('SearchInput', () => {
|
|||||||
await fireEvent.input(input, { target: { value: 'hel' } });
|
await fireEvent.input(input, { target: { value: 'hel' } });
|
||||||
vi.advanceTimersByTime(250);
|
vi.advanceTimersByTime(250);
|
||||||
expect(goto).toHaveBeenCalledTimes(1);
|
expect(goto).toHaveBeenCalledTimes(1);
|
||||||
expect(goto).toHaveBeenCalledWith('/search?q=hel', { replaceState: false, keepFocus: true });
|
expect(goto).toHaveBeenCalledWith('/search?q=hel', { replace: false, reset: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('typing while on /search uses replaceState=true', async () => {
|
test('typing while on /search uses replace=true', async () => {
|
||||||
state.pageUrl = new URL('http://localhost/search?q=h');
|
state.pageUrl = new URL('http://localhost/search?q=h');
|
||||||
render(SearchInput);
|
render(SearchInput);
|
||||||
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
||||||
await fireEvent.input(input, { target: { value: 'he' } });
|
await fireEvent.input(input, { target: { value: 'he' } });
|
||||||
vi.advanceTimersByTime(250);
|
vi.advanceTimersByTime(250);
|
||||||
expect(goto).toHaveBeenCalledWith('/search?q=he', { replaceState: true, keepFocus: true });
|
expect(goto).toHaveBeenCalledWith('/search?q=he', { replace: true, reset: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clearing input on /search calls goto("/search") (drops ?q=)', async () => {
|
test('clearing input on /search calls goto("/search") (drops ?q=)', async () => {
|
||||||
@@ -71,7 +71,7 @@ describe('SearchInput', () => {
|
|||||||
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
||||||
await fireEvent.input(input, { target: { value: '' } });
|
await fireEvent.input(input, { target: { value: '' } });
|
||||||
vi.advanceTimersByTime(250);
|
vi.advanceTimersByTime(250);
|
||||||
expect(goto).toHaveBeenCalledWith('/search', { replaceState: true, keepFocus: true });
|
expect(goto).toHaveBeenCalledWith('/search', { replace: true, reset: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Escape clears the input value', async () => {
|
test('Escape clears the input value', async () => {
|
||||||
@@ -88,6 +88,6 @@ describe('SearchInput', () => {
|
|||||||
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
const input = screen.getByRole('searchbox') as HTMLInputElement;
|
||||||
await fireEvent.input(input, { target: { value: 'a b&c' } });
|
await fireEvent.input(input, { target: { value: 'a b&c' } });
|
||||||
vi.advanceTimersByTime(250);
|
vi.advanceTimersByTime(250);
|
||||||
expect(goto).toHaveBeenCalledWith('/search?q=a%20b%26c', { replaceState: false, keepFocus: true });
|
expect(goto).toHaveBeenCalledWith('/search?q=a%20b%26c', { replace: false, reset: false });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { X, ListPlus, Plus, ListMusic, Heart } from 'lucide-svelte';
|
import { X, ListPlus, Plus, ListMusic, Heart } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { selection, clearSelection } from '$lib/selection/store.svelte';
|
import { selection, clearSelection } from '#lib/selection/store.svelte.js';
|
||||||
import { playNextMany, enqueueTracks } from '$lib/player/store.svelte';
|
import { playNextMany, enqueueTracks } from '#lib/player/store.svelte.js';
|
||||||
import { likeEntity, createLikedIdsQuery } from '$lib/api/likes';
|
import { likeEntity, createLikedIdsQuery } from '#lib/api/likes.js';
|
||||||
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { ChevronDown, Compass, House, LibraryBig } from 'lucide-svelte';
|
import { ChevronDown, Compass, House, LibraryBig } from 'lucide-svelte';
|
||||||
import { user, logout } from '$lib/auth/store.svelte';
|
import { user, logout } from '#lib/auth/store.svelte.js';
|
||||||
import { player } from '$lib/player/store.svelte';
|
import { player } from '#lib/player/store.svelte.js';
|
||||||
import { appName } from '$lib/branding';
|
import { appName } from '#lib/branding.js';
|
||||||
import MinstrelMark from './MinstrelMark.svelte';
|
import MinstrelMark from './MinstrelMark.svelte';
|
||||||
import NotificationBell from './NotificationBell.svelte';
|
import NotificationBell from './NotificationBell.svelte';
|
||||||
import PlayerBar from './PlayerBar.svelte';
|
import PlayerBar from './PlayerBar.svelte';
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
menuOpen = false;
|
menuOpen = false;
|
||||||
await logout();
|
await logout();
|
||||||
goto('/login', { replaceState: true });
|
goto('/login', { replace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function isActive(prefix: string): boolean {
|
function isActive(prefix: string): boolean {
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ const userState = vi.hoisted(() => ({
|
|||||||
} | null
|
} | null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { get value() { return userState.current; } },
|
user: { get value() { return userState.current; } },
|
||||||
logout: vi.fn().mockResolvedValue(undefined)
|
logout: vi.fn().mockResolvedValue(undefined)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// The bell's queries need a QueryClient the Shell tests do not provide; its own
|
// The bell's queries need a QueryClient the Shell tests do not provide; its own
|
||||||
// behaviour is covered in NotificationBell.test.ts.
|
// behaviour is covered in NotificationBell.test.ts.
|
||||||
vi.mock('$lib/api/notifications', async () => {
|
vi.mock('#lib/api/notifications.js', async () => {
|
||||||
const { mockQuery } = await import('../../test-utils/query');
|
const { mockQuery } = await import('../../test-utils/query');
|
||||||
return {
|
return {
|
||||||
createUnreadCountQuery: () => mockQuery({ data: 0 }),
|
createUnreadCountQuery: () => mockQuery({ data: 0 }),
|
||||||
@@ -38,7 +38,7 @@ vi.mock('$lib/api/notifications', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
import Shell from './Shell.svelte';
|
import Shell from './Shell.svelte';
|
||||||
import { logout } from '$lib/auth/store.svelte';
|
import { logout } from '#lib/auth/store.svelte.js';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -110,6 +110,6 @@ describe('Shell', () => {
|
|||||||
await fireEvent.click(screen.getByRole('button', { name: /alice/i }));
|
await fireEvent.click(screen.getByRole('button', { name: /alice/i }));
|
||||||
await fireEvent.click(screen.getByRole('menuitem', { name: /log out/i }));
|
await fireEvent.click(screen.getByRole('menuitem', { name: /log out/i }));
|
||||||
expect(logout).toHaveBeenCalledTimes(1);
|
expect(logout).toHaveBeenCalledTimes(1);
|
||||||
expect(goto).toHaveBeenCalledWith('/login', { replaceState: true });
|
expect(goto).toHaveBeenCalledWith('/login', { replace: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" module>
|
<script lang="ts" module>
|
||||||
import type { LidarrRequestStatus } from '$lib/api/types';
|
import type { LidarrRequestStatus } from '#lib/api/types.js';
|
||||||
|
|
||||||
type Tone = 'warning' | 'info' | 'success' | 'error';
|
type Tone = 'warning' | 'info' | 'success' | 'error';
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
createSnoozesQuery,
|
createSnoozesQuery,
|
||||||
snoozeSuggestion,
|
snoozeSuggestion,
|
||||||
unsnoozeSuggestion
|
unsnoozeSuggestion
|
||||||
} from '$lib/api/suggestions';
|
} from '#lib/api/suggestions.js';
|
||||||
import { createRequest } from '$lib/api/requests';
|
import { createRequest } from '#lib/api/requests.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { pushToast } from '$lib/stores/toast.svelte';
|
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||||
import DiscoverResultCard from './DiscoverResultCard.svelte';
|
import DiscoverResultCard from './DiscoverResultCard.svelte';
|
||||||
import type { ArtistSuggestion, SeedContribution, SuggestionSnooze } from '$lib/api/types';
|
import type { ArtistSuggestion, SeedContribution, SuggestionSnooze } from '#lib/api/types.js';
|
||||||
|
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
const queryStore = createSuggestionsQuery();
|
const queryStore = createSuggestionsQuery();
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
|
|||||||
return { ...actual, useQueryClient: () => ({ invalidateQueries: invalidateMock }) };
|
return { ...actual, useQueryClient: () => ({ invalidateQueries: invalidateMock }) };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock('$lib/api/suggestions', () => ({
|
vi.mock('#lib/api/suggestions.js', () => ({
|
||||||
createSuggestionsQuery: vi.fn(),
|
createSuggestionsQuery: vi.fn(),
|
||||||
createSnoozesQuery: vi.fn(),
|
createSnoozesQuery: vi.fn(),
|
||||||
snoozeSuggestion: vi.fn().mockResolvedValue(undefined),
|
snoozeSuggestion: vi.fn().mockResolvedValue(undefined),
|
||||||
unsnoozeSuggestion: vi.fn().mockResolvedValue(undefined)
|
unsnoozeSuggestion: vi.fn().mockResolvedValue(undefined)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/requests', () => ({
|
vi.mock('#lib/api/requests.js', () => ({
|
||||||
createRequest: vi.fn().mockResolvedValue({})
|
createRequest: vi.fn().mockResolvedValue({})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const pushToastMock = vi.fn();
|
const pushToastMock = vi.fn();
|
||||||
vi.mock('$lib/stores/toast.svelte', () => ({
|
vi.mock('#lib/stores/toast.svelte.js', () => ({
|
||||||
pushToast: (...args: unknown[]) => pushToastMock(...args)
|
pushToast: (...args: unknown[]) => pushToastMock(...args)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ import {
|
|||||||
createSnoozesQuery,
|
createSnoozesQuery,
|
||||||
snoozeSuggestion,
|
snoozeSuggestion,
|
||||||
unsnoozeSuggestion
|
unsnoozeSuggestion
|
||||||
} from '$lib/api/suggestions';
|
} from '#lib/api/suggestions.js';
|
||||||
import { createRequest } from '$lib/api/requests';
|
import { createRequest } from '#lib/api/requests.js';
|
||||||
import type { ArtistSuggestion, SuggestionSnooze } from '$lib/api/types';
|
import type { ArtistSuggestion, SuggestionSnooze } from '#lib/api/types.js';
|
||||||
|
|
||||||
const oneSeed: ArtistSuggestion = {
|
const oneSeed: ArtistSuggestion = {
|
||||||
mbid: 'mb1',
|
mbid: 'mb1',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// Single global toast renderer. Mounted once in +layout.svelte; pages call
|
// Single global toast renderer. Mounted once in +layout.svelte; pages call
|
||||||
// pushToast() from $lib/stores/toast.svelte. The {#key} re-mount on id
|
// pushToast() from #lib/stores/toast.svelte. The {#key} re-mount on id
|
||||||
// change is what makes screen readers re-announce a replacement toast.
|
// change is what makes screen readers re-announce a replacement toast.
|
||||||
import { toast } from '$lib/stores/toast.svelte';
|
import { toast } from '#lib/stores/toast.svelte.js';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if toast.value}
|
{#if toast.value}
|
||||||
|
|||||||
@@ -18,11 +18,11 @@
|
|||||||
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
||||||
import TrackMenuItem from './TrackMenuItem.svelte';
|
import TrackMenuItem from './TrackMenuItem.svelte';
|
||||||
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
import TrackMenuDivider from './TrackMenuDivider.svelte';
|
||||||
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '$lib/api/likes';
|
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '#lib/api/likes.js';
|
||||||
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
|
import { createMyQuarantineQuery, unflagTrack } from '#lib/api/quarantine.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { playNext, enqueueTrack, playRadio } from '$lib/player/store.svelte';
|
import { playNext, enqueueTrack, playRadio } from '#lib/player/store.svelte.js';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
track,
|
track,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const userState = vi.hoisted(() => ({
|
|||||||
| null
|
| null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: { get value() { return userState.current; } }
|
user: { get value() { return userState.current; } }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -21,20 +21,20 @@ vi.mock('$app/navigation', () => ({
|
|||||||
goto: vi.fn()
|
goto: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
|
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/playlists', () => emptyPlaylistsMock());
|
vi.mock('#lib/api/playlists.js', () => emptyPlaylistsMock());
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playNext: vi.fn(),
|
playNext: vi.fn(),
|
||||||
enqueueTrack: vi.fn(),
|
enqueueTrack: vi.fn(),
|
||||||
playRadio: vi.fn()
|
playRadio: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import TrackMenu from './TrackMenu.svelte';
|
import TrackMenu from './TrackMenu.svelte';
|
||||||
import { playNext, enqueueTrack, playRadio } from '$lib/player/store.svelte';
|
import { playNext, enqueueTrack, playRadio } from '#lib/player/store.svelte.js';
|
||||||
|
|
||||||
const track = makeTrack({ title: 'Roygbiv' });
|
const track = makeTrack({ title: 'Roygbiv' });
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { formatDuration } from '$lib/media/duration';
|
import { formatDuration } from '#lib/media/duration.js';
|
||||||
import { player, playQueue, enqueueTrack, playRadio } from '$lib/player/store.svelte';
|
import { player, playQueue, enqueueTrack, playRadio } from '#lib/player/store.svelte.js';
|
||||||
import { Plus, Check } from 'lucide-svelte';
|
import { Plus, Check } from 'lucide-svelte';
|
||||||
import LikeButton from './LikeButton.svelte';
|
import LikeButton from './LikeButton.svelte';
|
||||||
import TrackMenu from './TrackMenu.svelte';
|
import TrackMenu from './TrackMenu.svelte';
|
||||||
import {
|
import {
|
||||||
selection, toggleOne, selectRange
|
selection, toggleOne, selectRange
|
||||||
} from '$lib/selection/store.svelte';
|
} from '#lib/selection/store.svelte.js';
|
||||||
|
|
||||||
type PlayHandler = (tracks: TrackRef[], index: number) => void;
|
type PlayHandler = (tracks: TrackRef[], index: number) => void;
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
import { emptyLikesMock } from '../../test-utils/mocks/likes';
|
||||||
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/player/store.svelte', () => ({
|
vi.mock('#lib/player/store.svelte.js', () => ({
|
||||||
playQueue: vi.fn(),
|
playQueue: vi.fn(),
|
||||||
enqueueTrack: vi.fn(),
|
enqueueTrack: vi.fn(),
|
||||||
playRadio: vi.fn(),
|
playRadio: vi.fn(),
|
||||||
player: { current: undefined }
|
player: { current: undefined }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
|
||||||
|
|
||||||
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
|
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
|
||||||
|
|
||||||
import TrackRow from './TrackRow.svelte';
|
import TrackRow from './TrackRow.svelte';
|
||||||
import { playQueue, enqueueTrack, playRadio } from '$lib/player/store.svelte';
|
import { playQueue, enqueueTrack, playRadio } from '#lib/player/store.svelte.js';
|
||||||
import { selection, clearSelection } from '$lib/selection/store.svelte';
|
import { selection, clearSelection } from '#lib/selection/store.svelte.js';
|
||||||
|
|
||||||
const tracks: TrackRef[] = [
|
const tracks: TrackRef[] = [
|
||||||
makeTrack({ title: 'So What', duration_sec: 545 }),
|
makeTrack({ title: 'So What', duration_sec: 545 }),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { audioLoader, setAudioLoader, __resetAudioLoader } from './audioLoader';
|
import { audioLoader, setAudioLoader, __resetAudioLoader } from './audioLoader';
|
||||||
|
|
||||||
const sampleTrack: TrackRef = {
|
const sampleTrack: TrackRef = {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// no-op (the browser handles it via the prefetch <audio> element);
|
// no-op (the browser handles it via the prefetch <audio> element);
|
||||||
// Tauri can warm its native cache here.
|
// Tauri can warm its native cache here.
|
||||||
|
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
export type AudioLoader = {
|
export type AudioLoader = {
|
||||||
resolve: (track: TrackRef) => string;
|
resolve: (track: TrackRef) => string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { flushSync } from 'svelte';
|
import { flushSync } from 'svelte';
|
||||||
|
|
||||||
vi.mock('$lib/api/client', () => ({
|
vi.mock('#lib/api/client.js', () => ({
|
||||||
api: {
|
api: {
|
||||||
post: vi.fn().mockResolvedValue({ play_event_id: 'pe-1', session_id: 's-1' })
|
post: vi.fn().mockResolvedValue({ play_event_id: 'pe-1', session_id: 's-1' })
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
reportDuration,
|
reportDuration,
|
||||||
skipNext,
|
skipNext,
|
||||||
} from './store.svelte';
|
} from './store.svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
function track(id: string, dur = 200): TrackRef {
|
function track(id: string, dur = 200): TrackRef {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { player } from './store.svelte';
|
import { player } from './store.svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { getOrCreateClientId } from './clientId';
|
import { getOrCreateClientId } from './clientId';
|
||||||
import type { PlayStartedResponse, EventOkResponse } from '$lib/api/types';
|
import type { PlayStartedResponse, EventOkResponse } from '#lib/api/types.js';
|
||||||
|
|
||||||
// useEventsDispatcher installs $effect-based watchers that emit play events
|
// useEventsDispatcher installs $effect-based watchers that emit play events
|
||||||
// to /api/events. Call once from +layout.svelte's mount.
|
// to /api/events. Call once from +layout.svelte's mount.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
import { dbToLinear, gainDb, playingAsAlbum, MAX_BOOST_DB } from './gain';
|
import { dbToLinear, gainDb, playingAsAlbum, MAX_BOOST_DB } from './gain';
|
||||||
import type { NormalizationPrefs } from '$lib/api/normalization';
|
import type { NormalizationPrefs } from '#lib/api/normalization.js';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
const prefs = (over: Partial<NormalizationPrefs> = {}): NormalizationPrefs => ({
|
const prefs = (over: Partial<NormalizationPrefs> = {}): NormalizationPrefs => ({
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Loudness normalization math for the web player (M464 #4999). Pure, so the
|
// Loudness normalization math for the web player (M464 #4999). Pure, so the
|
||||||
// rules are tested without an <audio> element or an AudioContext.
|
// rules are tested without an <audio> element or an AudioContext.
|
||||||
|
|
||||||
import type { NormalizationPrefs, ReplayGain } from '$lib/api/normalization';
|
import type { NormalizationPrefs, ReplayGain } from '#lib/api/normalization.js';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
// The server's gains are to the ReplayGain 2.0 reference.
|
// The server's gains are to the ReplayGain 2.0 reference.
|
||||||
const REFERENCE_LUFS = -18;
|
const REFERENCE_LUFS = -18;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: {
|
user: {
|
||||||
get value() {
|
get value() {
|
||||||
return { id: 'test-user' };
|
return { id: 'test-user' };
|
||||||
@@ -11,8 +11,8 @@ vi.mock('$lib/auth/store.svelte', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const getReplayGain = vi.fn();
|
const getReplayGain = vi.fn();
|
||||||
vi.mock('$lib/api/normalization', async (orig) => ({
|
vi.mock('#lib/api/normalization.js', async (orig) => ({
|
||||||
...(await orig<typeof import('$lib/api/normalization')>()),
|
...(await orig<typeof import('#lib/api/normalization.js')>()),
|
||||||
getReplayGain: (ids: string[]) => getReplayGain(ids)
|
getReplayGain: (ids: string[]) => getReplayGain(ids)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
// what the track sounded like before leveling existed.
|
// what the track sounded like before leveling existed.
|
||||||
|
|
||||||
import { SvelteMap } from 'svelte/reactivity';
|
import { SvelteMap } from 'svelte/reactivity';
|
||||||
import { getReplayGain, type ReplayGain } from '$lib/api/normalization';
|
import { getReplayGain, type ReplayGain } from '#lib/api/normalization.js';
|
||||||
import { normalization } from '$lib/stores/normalization.svelte';
|
import { normalization } from '#lib/stores/normalization.svelte.js';
|
||||||
import { player } from './store.svelte';
|
import { player } from './store.svelte';
|
||||||
import { dbToLinear, gainDb, playingAsAlbum } from './gain';
|
import { dbToLinear, gainDb, playingAsAlbum } from './gain';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
import { flushSync } from 'svelte';
|
import { flushSync } from 'svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
function track(id: string): TrackRef {
|
function track(id: string): TrackRef {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { coverUrl } from '$lib/media/covers';
|
import { coverUrl } from '#lib/media/covers.js';
|
||||||
import {
|
import {
|
||||||
player,
|
player,
|
||||||
togglePlay,
|
togglePlay,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
export type PersistedPlayerState = {
|
export type PersistedPlayerState = {
|
||||||
v: 1;
|
v: 1;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
||||||
import { flushSync } from 'svelte';
|
import { flushSync } from 'svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
function track(id: string): TrackRef {
|
function track(id: string): TrackRef {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { TrackRef, RadioResponse } from '$lib/api/types';
|
import type { TrackRef, RadioResponse } from '#lib/api/types.js';
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '#lib/api/client.js';
|
||||||
import { user } from '$lib/auth/user.svelte';
|
import { user } from '#lib/auth/user.svelte.js';
|
||||||
import { sessionEnd } from '$lib/auth/sessionEnd.svelte';
|
import { sessionEnd } from '#lib/auth/sessionEnd.svelte.js';
|
||||||
import * as storage from '$lib/util/safeLocalStorage';
|
import * as storage from '#lib/util/safeLocalStorage.js';
|
||||||
import { readPersistedQueue, writePersistedQueue, clearPersistedQueue } from './persisted';
|
import { readPersistedQueue, writePersistedQueue, clearPersistedQueue } from './persisted';
|
||||||
|
|
||||||
export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
|
export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ import {
|
|||||||
__setShuffleRng,
|
__setShuffleRng,
|
||||||
restoreQueue
|
restoreQueue
|
||||||
} from './store.svelte';
|
} from './store.svelte';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { writePersistedQueue, clearPersistedQueue } from './persisted';
|
import { writePersistedQueue, clearPersistedQueue } from './persisted';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
|
|
||||||
vi.mock('$lib/auth/store.svelte', () => ({
|
vi.mock('#lib/auth/store.svelte.js', () => ({
|
||||||
user: {
|
user: {
|
||||||
get value() {
|
get value() {
|
||||||
return { id: 'test-user' };
|
return { id: 'test-user' };
|
||||||
@@ -345,7 +345,7 @@ import {
|
|||||||
toggleQueueDrawer,
|
toggleQueueDrawer,
|
||||||
closeQueueDrawer
|
closeQueueDrawer
|
||||||
} from './store.svelte';
|
} from './store.svelte';
|
||||||
import * as client from '$lib/api/client';
|
import * as client from '#lib/api/client.js';
|
||||||
|
|
||||||
describe('player store — enqueue + playRadio', () => {
|
describe('player store — enqueue + playRadio', () => {
|
||||||
test('enqueueTrack appends to a non-empty queue, _index unchanged', () => {
|
test('enqueueTrack appends to a non-empty queue, _index unchanged', () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
import type { PlaylistTrack } from '$lib/api/types';
|
import type { PlaylistTrack } from '#lib/api/types.js';
|
||||||
import { playlistTrackToRef } from './playlistTrackToRef';
|
import { playlistTrackToRef } from './playlistTrackToRef';
|
||||||
|
|
||||||
const row: PlaylistTrack = {
|
const row: PlaylistTrack = {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
// Callers filter nulls, which is what makes a missing file "get skipped"
|
// Callers filter nulls, which is what makes a missing file "get skipped"
|
||||||
// everywhere a queue is built rather than at each call site.
|
// everywhere a queue is built rather than at each call site.
|
||||||
|
|
||||||
import type { PlaylistTrack, TrackRef } from '$lib/api/types';
|
import type { PlaylistTrack, TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
export function playlistTrackToRef(row: PlaylistTrack): TrackRef | null {
|
export function playlistTrackToRef(row: PlaylistTrack): TrackRef | null {
|
||||||
if (!row.track_id || row.unavailable) return null;
|
if (!row.track_id || row.unavailable) return null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { TrackRef, PlaylistDetail } from '$lib/api/types';
|
import type { TrackRef, PlaylistDetail } from '#lib/api/types.js';
|
||||||
import { systemShuffle, getPlaylist } from '$lib/api/playlists';
|
import { systemShuffle, getPlaylist } from '#lib/api/playlists.js';
|
||||||
import { playlistTrackToRef } from './playlistTrackToRef';
|
import { playlistTrackToRef } from './playlistTrackToRef';
|
||||||
|
|
||||||
// #968: builds the self-heal closure for a system-playlist queue. On total
|
// #968: builds the self-heal closure for a system-playlist queue. On total
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test, beforeEach } from 'vitest';
|
import { describe, expect, test, beforeEach } from 'vitest';
|
||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
import { makeTrack } from '$test-utils/fixtures/track';
|
import { makeTrack } from '$test-utils/fixtures/track';
|
||||||
import {
|
import {
|
||||||
selection, toggleOne, selectRange, clearSelection
|
selection, toggleOne, selectRange, clearSelection
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TrackRef } from '$lib/api/types';
|
import type { TrackRef } from '#lib/api/types.js';
|
||||||
|
|
||||||
// Global multi-select state for track lists (album, library/liked,
|
// Global multi-select state for track lists (album, library/liked,
|
||||||
// search results, etc.). Reset on route change by the layout effect.
|
// search results, etc.). Reset on route change by the layout effect.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { queryClient } from '$lib/query/client';
|
import { queryClient } from '#lib/query/client.js';
|
||||||
import { qk } from '$lib/api/queries';
|
import { qk } from '#lib/api/queries.js';
|
||||||
import { user } from '$lib/auth/store.svelte';
|
import { user } from '#lib/auth/store.svelte.js';
|
||||||
|
|
||||||
// Inbound server events over SSE (#968). The web client's OUTBOUND half
|
// Inbound server events over SSE (#968). The web client's OUTBOUND half
|
||||||
// (play events) lives in player/events.svelte.ts; this is the inbound
|
// (play events) lives in player/events.svelte.ts; this is the inbound
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as storage from '$lib/util/safeLocalStorage';
|
import * as storage from '#lib/util/safeLocalStorage.js';
|
||||||
import {
|
import {
|
||||||
DEFAULT_NORMALIZATION,
|
DEFAULT_NORMALIZATION,
|
||||||
getNormalization,
|
getNormalization,
|
||||||
isNormalizationPrefs,
|
isNormalizationPrefs,
|
||||||
putNormalization,
|
putNormalization,
|
||||||
type NormalizationPrefs
|
type NormalizationPrefs
|
||||||
} from '$lib/api/normalization';
|
} from '#lib/api/normalization.js';
|
||||||
|
|
||||||
// The signed-in user's normalization preference. The server holds it; this
|
// The signed-in user's normalization preference. The server holds it; this
|
||||||
// keeps the last value seen in localStorage too, so the player levels the
|
// keeps the last value seen in localStorage too, so the player levels the
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as storage from '$lib/util/safeLocalStorage';
|
import * as storage from '#lib/util/safeLocalStorage.js';
|
||||||
|
|
||||||
const STORAGE_KEY = 'minstrel:theme';
|
const STORAGE_KEY = 'minstrel:theme';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import { render } from '@testing-library/svelte';
|
import { render } from '@testing-library/svelte';
|
||||||
import { setTheme } from '$lib/stores/theme.svelte';
|
import { setTheme } from '#lib/stores/theme.svelte.js';
|
||||||
import Probe from './__test__/Probe.svelte';
|
import Probe from './__test__/Probe.svelte';
|
||||||
|
|
||||||
describe('applyMetaThemeColor', () => {
|
describe('applyMetaThemeColor', () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import tokens from '$lib/styles/tokens.json';
|
import tokens from '#lib/styles/tokens.json';
|
||||||
import { resolvedTheme } from '$lib/stores/theme.svelte';
|
import { resolvedTheme } from '#lib/stores/theme.svelte.js';
|
||||||
|
|
||||||
// Browser address-bar tint matches the page background — colors.<mode>.obsidian
|
// Browser address-bar tint matches the page background — colors.<mode>.obsidian
|
||||||
// is the canonical background token, the same value the runtime CSS variables
|
// is the canonical background token, the same value the runtime CSS variables
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user