fix(swarm-stalwart): fix new issue on CI and allow videos on logic playwright

This commit is contained in:
Alejandro Roman committed 2026-09-09 22:45:08 +04:00
1 parent db2c1936d7
commit 4287dee96b
6 files changed
+41 -7

No files matched your search

+1 -2
View File
@@ -9,7 +9,6 @@
- "MAIL_RECIPIENT={{ lookup('mail_recipient') }}"
- "MAIL_TIMEOUT={{ lookup('email').timeout }}"
system_service_state: "{{ omit if (service_loader_preloading | default(false) | bool) else SYS_SERVICE_DEFAULT_STATE }}"
system_service_force_flush_instant: "{{ (MODE_DEBUG | bool) and not (service_loader_preloading | default(false) | bool) }}"
system_service_force_flush_instant: "{{ MODE_DEBUG | bool }}"
system_service_force_flush_final: false
- include_tasks: utils/once/flag.yml
@@ -132,6 +132,29 @@ function safeIsEnabled(name) {
}
}
/**
* Video-recording options for a hand-built browser context.
*
* Playwright applies the project's `use.video` setting only to the context the
* `page` fixture provides, so a context from `browser.newContext()` records
* nothing and its spec reaches the artefact with a trace but no video. Spread
* the result into the `newContext` options to follow the project setting.
*
* Args:
* testInfo: the spec's TestInfo; supplies the per-test output directory and
* the resolved project config.
*
* Returns:
* `{ recordVideo: { dir } }` when the project asks for video, else `{}`.
*/
function recordVideoOptions(testInfo) {
const configured = testInfo?.project?.use?.video;
const mode =
configured && typeof configured === "object" ? configured.mode : configured;
if (!mode || mode === "off") return {};
return { recordVideo: { dir: testInfo.outputDir } };
}
module.exports = {
decodeDotenvQuoted,
normalizeUrl,
@@ -141,4 +164,5 @@ module.exports = {
apiGetOnion,
safeSkipUnlessEnabled,
safeIsEnabled,
recordVideoOptions,
};
@@ -1,6 +1,6 @@
const { test, expect } = require("@playwright/test");
const { safeSkipUnlessEnabled, gotoOnion } = require("./personas");
const { safeSkipUnlessEnabled, gotoOnion, recordVideoOptions } = require("./personas");
const { roundcubeSsoLogin, roundcubeLogout, waitForEmailInMailbox } = require("./webmail");
const {
webmailBaseUrl,
@@ -51,7 +51,7 @@ async function leaveOpenDialogs(page) {
// answer. Login is via Keycloak SSO (Roundcube XOAUTH2 -> Stalwart), mirroring
// web-app-mailu. They are separate people: isolated browser contexts, and biber stays
// signed in so the answer lands in a session that never saw the outbound message.
test("stalwart: biber and the administrator exchange mail both ways", async ({ browser }) => {
test("stalwart: biber and the administrator exchange mail both ways", async ({ browser }, testInfo) => {
test.skip(isSplitRealmOidc(), "clearnet app with an onion OIDC issuer: unreachable from one browser");
safeSkipUnlessEnabled("sso");
expect(webmailBaseUrl, "WEBMAIL_BASE_URL must be set").toBeTruthy();
@@ -66,6 +66,7 @@ test("stalwart: biber and the administrator exchange mail both ways", async ({ b
const contextOptions = {
ignoreHTTPSErrors: true,
...(proxyServer ? { proxy: { server: proxyServer } } : {}),
...recordVideoOptions(testInfo),
};
const biberContext = await browser.newContext(contextOptions);
const adminContext = await browser.newContext(contextOptions);
@@ -1,6 +1,6 @@
const { test, expect } = require("@playwright/test");
const { safeSkipUnlessEnabled, gotoOnion } = require("./personas");
const { safeSkipUnlessEnabled, gotoOnion, recordVideoOptions } = require("./personas");
const { roundcubeSsoLogin, roundcubeLogout } = require("./webmail");
const { webmailBaseUrl, biberUsername, biberPassword } = require("./env");
const { resolveTimeout, isSplitRealmOidc } = require("./timeouts");
@@ -17,7 +17,7 @@ const { resolveTimeout, isSplitRealmOidc } = require("./timeouts");
// and never reach the gateway.
const FOREIGN_ONION = "recipient@nexusprobe7xk3mjqzvhbnd4rlyugc2pfe6sotai5w.onion";
test("biber: a .onion recipient is accepted and routed through the Tor gateway", async ({ browser }) => {
test("biber: a .onion recipient is accepted and routed through the Tor gateway", async ({ browser }, testInfo) => {
test.skip(isSplitRealmOidc(), "clearnet app with an onion OIDC issuer: unreachable from one browser");
safeSkipUnlessEnabled("tor");
safeSkipUnlessEnabled("sso");
@@ -29,6 +29,7 @@ test("biber: a .onion recipient is accepted and routed through the Tor gateway",
const context = await browser.newContext({
ignoreHTTPSErrors: true,
...(proxyServer ? { proxy: { server: proxyServer } } : {}),
...recordVideoOptions(testInfo),
});
try {
+7 -1
View File
@@ -12,7 +12,13 @@
changed_when: false
retries: 12
delay: 5
until: stalwart_jmap_raw.rc == 0
until: >-
stalwart_jmap_raw.rc == 0
and (
(stalwart_jmap_raw.stdout | from_json).methodResponses[0][0] != 'error'
or (stalwart_jmap_raw.stdout | from_json).methodResponses[0][1].type
not in STALWART_JMAP_TRANSIENT_ERRORS
)
timeout: "{{ lookup('timeout', 120) | int }}"
# nocheck: include-uniform-apply-keyword — seven call sites include this file; the in-file no_log is the SPOT, per-site apply would repeat it 7×
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
+3
View File
@@ -15,6 +15,9 @@ STALWART_LOG_DIR: "{{ lookup('volume', application_id, 'data').target }}"
STALWART_JMAP_APPLICATION_GET: >-
{{ {'using': ['urn:ietf:params:jmap:core', 'urn:stalwart:jmap'],
'methodCalls': [['x:Application/get', {'ids': none}, 'c0']]} | to_json }}
STALWART_JMAP_TRANSIENT_ERRORS:
- serverUnavailable
- rateLimit
STALWART_CONTAINER_ADDRESS: "{{ lookup('container_address', application_id, 'stalwart') }}"
STALWART_DOMAIN: "{{ lookup('config', application_id, 'services.stalwart.domain') }}"