Read, starred and position move to entry_state; subscriptions carry each person's keywords, auto-download, explicit and per-scan limit. The feed list and unread counts are per person, and the existing library is adopted by the admin on first start. The feed URL, folder and schedule stay shared and admin-only: one file serves everyone, so they describe the file rather than a preference. Scanning merges subscribers' wants -- anyone wanting an item is enough -- via merge_policy, which is pure and tested. Also: the test fixture wiped its data directory from every Playwright worker, deleting the database out from under the running daemon. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
62 lines
2.1 KiB
JavaScript
62 lines
2.1 KiB
JavaScript
// Builds a scratch config and data dir so the browser tests drive a real daemon with
|
|
// known feeds, rather than whatever happens to be on the machine.
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const os = require('os');
|
|
|
|
const root = path.join(os.tmpdir(), 'ipx-ui-test');
|
|
const TOKEN = 'testtokentesttokentesttoken12345'; // fixed, so tests need not scrape a log
|
|
|
|
// Called from playwright.config.js at load time, NOT as globalSetup: Playwright starts
|
|
// webServer *before* globalSetup, so a config written there does not exist yet when the
|
|
// daemon launches -- it would fall back to the real config and fight the live daemon.
|
|
// Playwright imports this config again in every worker process, so prepare() runs more
|
|
// than once per suite. Wiping on the second call deleted the data directory out from under
|
|
// the running daemon: it kept serving from the unlinked inode, while anything else opening
|
|
// that path -- the CLI, a query -- got a brand new empty database and disagreed with it.
|
|
function prepare() {
|
|
// Only the process that launches the run may wipe. A worker gets TEST_WORKER_INDEX.
|
|
if (process.env.TEST_WORKER_INDEX !== undefined || process.env.PW_WORKER_INDEX !== undefined) {
|
|
return;
|
|
}
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
for (const d of ['config', 'data', 'downloads']) {
|
|
fs.mkdirSync(path.join(root, d), { recursive: true });
|
|
}
|
|
fs.writeFileSync(path.join(root, 'config', 'config.toml'), `
|
|
[general]
|
|
download_dir = "${path.join(root, 'downloads')}"
|
|
socket = "${path.join(root, 'ipx.sock')}"
|
|
schedule = "every 60m"
|
|
max_new_per_check = 1
|
|
|
|
[torrent]
|
|
enabled = false
|
|
|
|
[web]
|
|
enabled = true
|
|
bind = "127.0.0.1:8791"
|
|
token = "${TOKEN}"
|
|
|
|
[feeds.test-show]
|
|
url = "http://127.0.0.1:8792/show.xml"
|
|
auto_download = true
|
|
|
|
# Downloads its image, so the UI has a file that is not playable to deal with.
|
|
[feeds.picture-blog]
|
|
url = "http://127.0.0.1:8792/pics.xml"
|
|
auto_download = true
|
|
media_types = ["image"]
|
|
|
|
[feeds.multi-show]
|
|
url = "http://127.0.0.1:8792/multi.xml"
|
|
auto_download = true
|
|
|
|
[feeds.test-subscriptions]
|
|
url = "http://127.0.0.1:8792/subs.opml"
|
|
auto_download = false
|
|
`);
|
|
}
|
|
|
|
module.exports = { prepare, root, TOKEN };
|