Commit Graph
21 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 8dce5b3caa fix(web-svc-collabora): name the WOPI host and follow the vhost's TLS state
The role restricted the WOPI hosts with `domain` and with
`--o:net.wopi_allowed_hosts`. The distroless image that #775 brought in
applies neither. `domain` was translated by the start script of the older
image, which this one does not have, and coolwsd knows no setting called
`net.wopi_allowed_hosts`. Its startup log listed that key among the
overrides and no trusted WOPI host, so the server ran with the policy the
image ships instead of the one the role declares.

The allowlist now goes through `aliasgroup1`, the variable coolwsd reads
itself. It carries the origin of the Nextcloud domain as the deployment
resolves it, with scheme and port, so it also holds in a Tor variant. The
old expression was built from DOMAIN_PRIMARY and would not have matched an
onion address.

No spec opened a document, which is why this went unnoticed. The
richdocuments spec now uploads a document, opens it and waits for the
`status:` frame on the editor websocket.

Its first run on the Tor variant found a second defect. `ssl.termination`
was fixed to true, so coolwsd advertised https URLs in its discovery while
an onion vhost only serves http, and the browser never reached the editor.
The setting now follows the TLS state of the Collabora vhost.

Closes #802

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:18:04 +02:00
kevinveenbirkenbachandClaude Opus 5 27a7e8cceb test(nextcloud): retry the standalone login once inside the helper
Three Playwright workers run three spec files at once, and on a fresh instance the first OIDC callback is what provisions the Nextcloud account. Concurrent callbacks then race on INSERT INTO oc_users for the same uid: one wins, the losers answer HTTP 500. The exception is SQLSTATE[23000] 1062 Duplicate entry 'keycloak-<sub>' for key 'PRIMARY', raised from the sociallogin app's ProviderService through OC\User\Manager::createUser, and it is visible both in the CI rescue bundle of run 34653754175 and in the container log of a local variant-1 deploy.

The race is self-closing: once any insert wins, the window is shut for the rest of the run. It has never turned a job red, because Playwright's retries absorb it and a flaky-only run exits 0. What it costs is a rerun of the whole spec file, 1.8 minutes for fileslibreofficeedit against a 42 second first attempt, plus a flaky line in every report.

Retrying inside the helper keeps that cost to one login. The base entry point now makes a second attempt after a five second pause, which resolveTimeout stretches to twenty five over Tor, and the observed failures abort after thirty to forty two seconds against a nine hundred second budget, so the second attempt has room. A persistent failure still fails, and the first attempt's message is appended to the second rather than swallowed, which the old wrapper's bare catch did not do.

loginToStandaloneNextcloudWithRetry is gone: a helper of that name beside a base that retries is a trap, and its five callers now reach the same two attempts through the base. The other forty eight addon specs gain the retry without being touched.

Rejected alternatives, each against a written constraint: a serial globalSetup would have to live in the playwright.config.js that 87 roles share and would force ignoreHTTPSErrors into the central use block; PLAYWRIGHT_WORKERS=1 reverses the measured Tor tuning in playwright_workers.py and leaks through an unnamed include_vars into every later role; pre-creating the account collides with prevent_create_email_exists and turns a one-off flake into a permanent login refusal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 11:08:15 +02:00
kevinveenbirkenbachandClaude Opus 5 c215aca058 fix(nextcloud): route sociallogin through the tor proxy, fail fast on a refused login callback, stop leaking plugin config
Run 34421944328 Nextcloud#1 timed out "waiting for a signed-in Nextcloud
shell after the login redirect". 4164f37981 turned sso on in variant 1
while ldap stays off there, so that round logs in through sociallogin
instead of oidc_login - the first variant, on this branch or on main,
that runs sociallogin against an onion issuer. oidc_login goes through
Nextcloud's HTTP client, which honours the proxy in proxy.config.php;
sociallogin builds its own client and reads only
social_login_http_client['proxy'], so its token exchange never reached
the onion Keycloak. The template now sets the same tor SOCKS proxy for
it, inside the existing "sso on and the OIDC host is an onion" branch.

The spec only treated 5xx documents as a page failure, so a refused
callback surfaced as a 60s (x5 over tor) wait for a shell that could not
come. A 4xx on /apps/sociallogin/ or /apps/oidc_login/ now counts as
the page itself failing, reported without the query so no code leaks.

The plugin loop printed every item, including the OIDC client secret,
into the include line of a public CI log; it now labels by plugin key and
honours MASK_CREDENTIALS_IN_LOGS.

_shared.js grew past 250 lines; its page and modal helpers move to
_page.js and _shared.js re-exports them, with an identical export
surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 13:21:39 +02:00
kevinveenbirkenbachandClaude Opus 5 de1efdf0d5 fix(nextcloud): let the Mastodon connect click survive the first-run wizard
The addon spec reached the per-user connected-accounts page, resolved the
"Connect to Mastodon" button and found it visible, enabled and stable, then
spent its whole 30s click timeout being refused:

    <div id="firstrunwizard" class="first-run-wizard modal-mask modal-mask--opaque">
    subtree intercepts pointer events

The single best-effort dismissal before the click is bounded and returns as
soon as two checks find no modal, so a wizard that appears late outlives it.
That the wizard, not the bridge, is the problem shows in the same job: the
spec passed eleven minutes later against the same stack, and passes in both
swarm rows.

The retry loop for exactly this interception already exists, written for the
user-menu click and named after it. Widen the name, take any locator, and use
it here. Nothing outside the shared module referenced the old name, so it is
renamed rather than aliased.

This does not turn the job green: it also fails the onlyoffice addon, which
is a separate cause.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:59:06 +02:00
kevinveenbirkenbachandClaude Opus 5 c14b1d13d6 fix(nextcloud): compare the Mastodon partner host against the deployed one
integration_mastodon asserted the provisioned oauth_instance_url resolved to
the literal "microblog.infinito.example". That value is not fixed: the addon
hook writes lookup('tls','web-app-mastodon','url.base') into it
(meta/addons/integration_mastodon.yml:12), so on an onion node the plugin
correctly received http://microblog.<onion>/ and the spec failed for being
right. The product was never wrong; the assertion was pinned to a clearnet
name.

The partner URL now reaches the spec the way every sibling bridge spec already
gets it, from the same lookup the hook uses:

  playwright.env.j2   MASTODON_BASE_URL, gated on services.mastodon.enabled
  _shared.js          read and exported as mastodonBaseUrl
  the spec            compares host against host, not against a literal

web-app-fediwall already wires MASTODON_BASE_URL from this exact lookup
(templates/playwright.env.j2:12), so the name and the source are the
established ones; this adds the gate, so the variable renders empty instead of
naming an undeployed partner.

The guard in front of the comparison is not decoration: without it an empty
MASTODON_BASE_URL would reach new URL("") and abort the spec with a TypeError
instead of an assertion anyone can read.

The evidence that this is the whole difference: integration_peertube runs the
identical shape against shared.env.peertubeBaseUrl and PASSED in the same red
run, on the same onion node where integration_mastodon failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:28 +02:00
kevinveenbirkenbachandClaude Opus 5 8c299f8e34 Merge PR #495 (nextcloud image bump + NC34 fixes) into feature/svc-net-tor
Covered by PR #380 rather than merged on its own. This branch already carries
the Tor onion-service provider across the whole role set, and #495 touches the
same nextcloud Playwright specs; landing them separately would mean resolving
the same conflicts twice, once here and once on main. Reviewing #495 as part of
#380 keeps the resolution in one place.

One conflict, in the external-sites addon spec. Nextcloud 34 renamed the admin
section from #external to #external-vue and replaced the add-site control with
a 'New site' button, so the incoming selectors win — retargeting them is what
the PR is for. This branch's resolveTimeout wrappers stay on both assertions.

make quality green on the merge result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 14:47:00 +02:00
kevinveenbirkenbachandClaude Opus 5 a14a347625 fix(nextcloud): attach the 500 detector before the login navigation
waitForVisibleCandidate can report a failed document response instead of
a bare selector timeout, but it attached the listener itself, and its
first call comes after loginToStandaloneNextcloud's goto has already
consumed both the redirect and the response. Playwright delivers only
events that arrive after attachment, so the detector was structurally
blind to the one navigation it exists for.

Attach it before the goto. trackServerErrors is idempotent through its
WeakMap, so the later call is a no-op and the existing evaluation in the
wait loop is untouched.

A 500 login page now fails in ~130ms naming the endpoint, rather than
after 60s of waiting for selectors that cannot appear on an error page --
which is what pointed the recent OIDC investigation at the proxy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 00:50:49 +02:00
kevinveenbirkenbachandClaude Opus 5 a0f0c9c967 fix(nextcloud): retarget selectors dropped by NC34
The external app's admin UI became a Vue app in v9.0.1 — the only
release declaring min/max-version 34, and the one CI installs. Its
template is now just <div id="external-vue"></div>, so the two hard
assertions on #external and #add_external_site (v8.0.1 markup) could
only time out. Match the mount point and the NcButton label instead;
both stay sharp, since an unmounted #external-vue has zero height and
the button exists only once the component ran.

Drop #app-content from the shared shell candidates as well: NC34 emits
that id in apps/settings/templates/help.php alone, a route no spec
visits. Dead alternatives inside an OR-list are how the login breakage
stayed invisible — #header-start__appmenu sat there long after Vue
started replacing its mount node.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 16:53:47 +02:00
kevinveenbirkenbachandClaude Opus 5 6e0b219079 fix(nextcloud): detect the NC34 shell after login
Nextcloud 34 moved the app links into the waffle popover, so
`a[href*="/apps/files"]` is absent until the menu is opened, and
`#header-start__appmenu` is replaced by `nav.app-menu` on mount in
33 and 34 alike. Both post-login markers were therefore gone and
every spec died in `loginToStandaloneNextcloud`, although the login
itself succeeded (POST /login 303, GET /apps/dashboard/ 200).

Match on the header chrome and the dashboard root instead — present
in both versions, absent from the guest layout, so the post-logout
assertion in test-login-biber-ldap.js stays sharp.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 13:46:26 +02:00
kevinveenbirkenbach 342de43111 Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
# Conflicts:
#	roles/svc-ai-ollama/tasks/01_manager_ops.yml
#	roles/web-app-decidim/templates/compose.yml.j2
#	roles/web-app-joomla/templates/compose.yml.j2
#	roles/web-app-matrix/files/flavor/ansible/runner/bootstrap.service
#	roles/web-app-matrix/templates/flavor/compose/services.yml.j2
#	roles/web-app-nextcloud/files/playwright/_shared.js
#	roles/web-svc-cdn/tasks/01_assets.yml
#	scripts/tests/deploy/swarm/utils/trigger_units.sh
#	scripts/tests/deploy/utils/filesystem/docker_dataroot.sh
2026-08-05 02:32:58 +02:00
kevinveenbirkenbach 56e4792ca5 test(nextcloud): fail fast on document 5xx 2026-08-04 08:37:39 +02:00
kevinveenbirkenbachandClaude Opus 4.8 5e194d885d test(web-app-nextcloud): onion-harden the Playwright suite
Scale the login helper's waitForVisibleCandidate call-sites and the Sign In / Log out clicks through resolveTimeout, and wait for network idle after logout so the slow keycloak end-session redirect chain over Tor settles before the logged-out state is probed.

Skip the Talk-admin and SeaweedFS-filer specs on onion targets (WebRTC HPB/STUN/TURN and a clearnet-only filer UI). Assert the XWiki partner host against XWIKI_BASE_URL from the env instead of a hard-coded clearnet host, so the check holds on both clearnet and onion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:50:09 +02:00
kevinveenbirkenbachandClaude Opus 4.8 35dee215c8 test(playwright): route all timeouts through resolveTimeout for onion-compat
Add the global TIMEOUT_FACTOR and a shared timeouts.js helper: resolveTimeout(base) scales by TIMEOUT_FACTOR and, over a .onion target, an extra multiplier, so Tor's per-request latency no longer trips hard-coded timeouts. Migrate every raw timeout in the specs (timeout:/timeout=/setTimeout/waitForTimeout) to resolveTimeout, stage the helper in all three staging paths, and add test_no_raw_timeouts to forbid raw numeric timeouts. Clearnet behaviour is unchanged (factor 1: resolveTimeout(N) === N).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 10:38:06 +02:00
kevinveenbirkenbachandClaude Opus 4.8 4c1b41418d feat(nextcloud): zammad OAuth provisioning + strengthen peertube addon spec
Provision a Doorkeeper OAuth application on the partner Zammad and write its client_id/client_secret into integration_zammad (Nextcloud-encrypted), so the per-user OAuth connect flow has the credentials the generic SPOT cannot supply. Register both the pretty-URL and /index.php oauth-redirect variants, and drop the hashed-secret fallback so a missing plaintext secret fails loudly instead of persisting an unusable hash.

Strengthen the integration_peertube spec: pin the admin 'instances' value to the exact deployed partner host (new PEERTUBE_BASE_URL) instead of merely 'not Nextcloud', and add a functional OCS search-providers check that fails when the PeerTube provider is not registered. PeerTube has no per-user OAuth (verified upstream: no client_id/oauth_instance_url config, no /oauth/authorize route), so the removed Connect->/oauth/authorize assertion tested a non-existent flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 02:50:57 +02:00
kevinveenbirkenbachandClaude Opus 4.8 48fbf53c39 test(playwright): nextcloud shared spec infra — login robustness, env exports
- loginToStandaloneNextcloud signed-in-shell wait 60s -> 120s so cold
  first-deploys do not time out mid-login
- export moodleBaseUrl from _shared and render MOODLE_BASE_URL /
  DISCOURSE_BASE_URL (service-gated) for the moodle/discourse integration specs
- drop NEXTCLOUD_RICHDOCUMENTS_EXPECTED_WOPI_URL (richdocuments spec no longer
  pins the WOPI host)
- strip narrative comments

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:03:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 24d4ab8e4a refactor(sso)\!: collapse oauth2 + oidc into unified services.sso block (req 21)
Implements docs/requirements/021-sso-flavor-migration.md atomically:

* Schema: every role's meta/services.yml replaces the parallel
  `oauth2:` / `oidc:` top-level blocks with a single `sso:` block
  carrying `enabled`, `shared`, and a `flavor` discriminator
  (`oidc` | `oauth2` | `saml`; default `oidc`). Flavor-specific
  sub-keys live under `sso.<flavor>.*` (`sso.oauth2.{origin,acl,
  allowed_groups}`, `sso.oidc.plugin`).
* Provider role: roles/web-app-oauth2-proxy/ is deleted in its
  entirety; its 5 sidecar templates move under
  roles/web-app-keycloak/templates/sso_proxy/, and the per-consumer
  rendering task lives at roles/web-app-keycloak/tasks/sso_proxy.yml.
  web-app-keycloak now declares `provides: sso`.
* Runtime: new resolver utils/roles/applications/services/sso.py
  centralises the SSO predicates (`is_enabled`, `is_proxy_gated`,
  `is_oidc_native`) and the oauth2 sub-values
  (`oauth2_origin_{host,port}`, `oauth2_acl`, `oauth2_allowed_groups`).
  Three lookup plugins consume the resolver: `sso` (single-app
  predicate access), `sso_proxy_consumers` (enumerates oauth2-flavored
  consumers), `sso_oidc_plugin` (renamed from `oidc_flavor`, Nextcloud
  plugin selector). sys-stk-backend, sys-svc-compose, sys-svc-proxy
  and plugins/filter/compose_volumes.py all gate on
  `lookup('sso', ..., 'is_proxy_gated')` instead of manually combining
  `enabled` and `flavor`.
* Identifier renames repo-wide: `services.<entity>.ports.local.oauth2`
  → `ports.local.sso`; `PORT_BANDS.local.oauth2` → `PORT_BANDS.local.sso`;
  `OAUTH2_PROXY_*` env/Jinja vars → `SSO_PROXY_*`;
  `oauth2_proxy_cookie_secret` → `sso_proxy_cookie_secret`;
  `OAUTH2_SERVICE_ENABLED` + `OIDC_SERVICE_ENABLED` collapse into
  `SSO_SERVICE_ENABLED`; Playwright gating helpers
  (isServiceEnabled/skipUnlessServiceEnabled/requireService/safeIsEnabled/
  safeSkipUnlessEnabled) move from `oidc`/`oauth2` to `sso`.
* Dual-block resolution: per req's Background-Table, web-app-bookwyrm
  is `flavor: oauth2`; web-app-gitea and web-app-friendica keep
  `flavor: oauth2` with their existing `oauth2.acl.blacklist`
  (/user/login) and `oauth2.acl.whitelist` (federation + discovery
  endpoints) — preserving the operative oauth2-proxy gating.
* Tests: the two mutual-exclusion guards
  (tests/integration/iam/oauth2_oidc/test_mutual_exclusive.py and
  test_acl_mutual_exclusive.py) are deleted per Decision #10;
  test_oauth2_contract.py is renamed and rewritten to
  test_sso_contract.py, asserting that `flavor: oauth2` roles carry
  `oauth2.origin.{host,port}` plus `ports.local.sso`; test_sso.py
  (web-app integration) now requires the unified `sso:` block with an
  explicit flavor or a `# nocheck: sso` opt-out; new
  tests/lint/repository/no_legacy_sso_paths/ guards the source tree
  against re-introducing the legacy strings.
* Docs: req 21 itself has every AC checked off, and reqs 006, 008,
  009, 013, 015, 016, 019, 020 plus 10+ role READMEs and
  docs/contributing/actions/testing/suppression.md are updated to the
  unified SSO naming.

Empirically validated end-to-end against the validation app set
(web-app-keycloak, web-app-nextcloud, web-app-bookwyrm, web-app-gitea,
web-app-friendica, web-app-dashboard) in matrix variants 0 and 1; v2
also passes the 4 target roles' Playwright after a documented
pre-existing KC_HOSTNAME-drift workaround (separate concern, not
caused by this refactor).

BREAKING CHANGE: every consumer of `services.oauth2.*`,
`services.oidc.*`, `OAUTH2_PROXY_*`, `OAUTH2_SERVICE_ENABLED`,
`OIDC_SERVICE_ENABLED`, `oauth2_proxy_cookie_secret`,
`oauth2_proxy_application_id`, `ports.local.oauth2`, the
`web-app-oauth2-proxy` role, or the legacy gating helper service names
(`oidc`/`oauth2`) must migrate to the unified `sso` shape documented
in docs/requirements/021-sso-flavor-migration.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 12:59:59 +02:00
kevinveenbirkenbach 0336467b23 fix(web-app-nextcloud): broaden NC 33 login locator + scope biber-ldap logout assertion
NC 33's login form labels the username textbox simply "Account name"
(no "or email" suffix), so the prior regex matched nothing and every
login-via-native-form spec timed out at the 5-s default. Broaden the
pattern to accept either label shape.

The biber-LDAP scenario also failed at the logout assertion: the
universal-logout patch from sys-front-inj-logout rewrites the click
target to Keycloak's logout endpoint, which in user_ldap-only mode
ends on Keycloak's "You are logged out" info page rather than NC's
/login. Replace the negative shell-absence check with a positive
poll on the Keycloak logout URL that matches what the universal-logout
patch actually does.
2026-05-19 17:40:59 +02:00
kevinveenbirkenbachandClaude Opus 4.7 7ef0fca5c0 fix(web-app-nextcloud): hardcode role-id in legacy_login_mask lookup + anchor submit-button regex
meta/services.yml — switch `lookup('config', application_id, ...)` to the
spelled-out role id `'web-app-nextcloud'`. `application_id` is undefined
when meta/services.yml is rendered (the applications registry is built
before any role-vars binding), which made the lookup return the raw
unrendered Jinja string instead of the resolved oidc.enabled value, and
in turn flipped legacy_login_mask.enabled to false in variant 1 where
it should have been true. The matching `twofactor_*` plugin entries in
the same file already use the hardcoded form for the same reason.

files/playwright/_shared.js — anchor `nextcloudCredentialSubmitPattern`
to `^(sign in|log in)$` so it matches only the credential-form submit
button. With auth.webauthn.enabled=true (the post-fix variant-1 state)
Nextcloud renders an additional `Log in with a device` button next to
the credential `Log in`, which the previous unanchored pattern matched
too — strict-mode violation, test fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:36:04 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0a170669d3 fix(lint-javascript): extend playwright plugin scope to helper modules + drop dead nextcloud export
ESLint reported 2 errors after the playwright-spec split refactors:
- `playwright/no-wait-for-navigation` rule "not found" at the inline
  disable directive in web-app-fediwall/_shared.js, because the
  playwright plugin block was scoped to `playwright.spec.js` only.
- `enterNextcloudLoginThroughVisibleEntryPoint` undefined in the
  web-app-nextcloud `_shared.js` module.exports — a refactor
  (daefcf146) intentionally dropped the helper but left the export
  reference behind.

Widen the playwright plugin glob to `roles/**/files/playwright/**/*.js`
so the same rule set applies to the now-common per-test submodules
(_shared.js, test-*.js splits), and remove the dangling export.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 01:09:02 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2b498b814d refactor(web-app-nextcloud): co-locate Talk admin spec with its gate
Move the Talk env vars, settings-text scraping helpers, spreed
test-button verifier, and the former `verifyNextcloudTalkAdminSettings`
flow out of `_shared.js` into `test-talk-admin-settings.js` so the gate
flag, env-presence guard, and assertion logic live in a single module.
Drop the redundant `verifyNextcloudTalkAdminSettings` calls from the
admin OIDC and native login specs — the dedicated Talk test now owns
that coverage. Add `talk: true` / `talk: false` variant pins for the
new dynamic `services.talk.enabled` flag so coverage stays complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 01:09:02 +02:00
kevinveenbirkenbach 8f828bb168 refactor(web-app-nextcloud): split playwright spec into per-test modules + add flavor-gated admin/biber login coverage
Mirror the taiga/pixelfed pattern: `playwright.spec.js` shrinks from a
834-LOC monolith to a 28-LOC orchestrator that wires `beforeEach` and
require()s one test module per scenario; all shared env, locator,
modal, login/logout and Talk-admin verification logic moves to
`_shared.js`. Each test stays atomar and individually inspectable via
its filename.

Login coverage is now persona × flavor instead of one monolithic
"nextcloud login" that implicitly assumed OIDC:

- test-login-admin-oidc.js   skip \!oidcEnabled
- test-login-admin-native.js skip oidcEnabled       (uses
                                                     NEXTCLOUD_DIRECT_LOGIN_PASSWORD
                                                     via the shared
                                                     login helper)
- test-login-biber-oidc.js   skip \!oidcEnabled
- test-login-biber-ldap.js   skip oidcEnabled || \!ldapEnabled (biber is
                                                     not autoprovisioned
                                                     in pure-native NC)

Persona scenarios (guest / biber / administrator post-login flows) keep
delegating to the shared `runGuestFlow` / `runBiberFlow` / `runAdminFlow`
helpers and stay universal across all variants.

Also drops an unused helper (`enterNextcloudLoginThroughVisibleEntryPoint`)
that the deleted always-on `test-nextcloud-login.js` was the only
consumer of.
2026-05-18 01:09:01 +02:00