@remit/web-client 0.0.243 → 0.0.245
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/settings/QuarantinePanel.delimiter.test.ts +41 -11
- package/src/lib/calendar-route.ts +0 -2
- package/src/lib/config-import.ts +0 -5
- package/src/lib/i18n.ts +1 -1
- package/src/test-support/dom.ts +28 -0
- package/public/locales/de/errors.json +0 -1
- package/public/locales/de/settings.json +0 -1
- package/public/locales/en/errors.json +0 -1
- package/public/locales/en/settings.json +0 -1
- package/public/locales/fr/errors.json +0 -1
- package/public/locales/fr/settings.json +0 -1
- package/public/locales/nl/errors.json +0 -1
- package/public/locales/nl/settings.json +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.245",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
|
|
6
6
|
"exports": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import assert from "node:assert/strict";
|
|
11
11
|
import { afterEach, describe, it } from "node:test";
|
|
12
|
+
import { meOperationsListQuarantineOptions } from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
12
13
|
import { createElement } from "react";
|
|
13
14
|
import { createDomHarness, type DomHarness } from "../../test-support/dom";
|
|
14
15
|
import { type HttpMock, httpError, mockFetch } from "../../test-support/http";
|
|
@@ -62,26 +63,47 @@ afterEach(() => {
|
|
|
62
63
|
http = undefined;
|
|
63
64
|
});
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The quarantine list is the read that gives the pane something to paint: until
|
|
68
|
+
* it has landed, an assertion that no path is showing passes because there are
|
|
69
|
+
* no rows at all, which is not what any of these cases claims to test.
|
|
70
|
+
*/
|
|
71
|
+
const quarantineLanded = (view: DomHarness): boolean =>
|
|
72
|
+
view.queryClient.getQueryState(meOperationsListQuarantineOptions().queryKey)
|
|
73
|
+
?.status === "success";
|
|
74
|
+
|
|
75
|
+
const mount = async (
|
|
76
|
+
mailboxes: () => unknown,
|
|
77
|
+
settled: (view: DomHarness) => boolean,
|
|
78
|
+
description: string,
|
|
79
|
+
): Promise<DomHarness> => {
|
|
66
80
|
http = mockFetch((call) => {
|
|
67
81
|
if (call.path.endsWith("/config")) return CONFIG;
|
|
68
82
|
if (call.path.endsWith("/mailboxes")) return mailboxes();
|
|
69
83
|
if (call.path.endsWith("/quarantine")) return QUARANTINE;
|
|
70
84
|
return {};
|
|
71
85
|
});
|
|
72
|
-
|
|
73
|
-
harness
|
|
86
|
+
const view = createDomHarness();
|
|
87
|
+
harness = view;
|
|
88
|
+
view.renderApp(createElement(QuarantinePanel));
|
|
74
89
|
// Two waves: the mailbox-list fan-out is keyed on the accounts `/config`
|
|
75
|
-
// returns, so it cannot even start until that first read has settled.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await
|
|
79
|
-
return
|
|
90
|
+
// returns, so it cannot even start until that first read has settled. How
|
|
91
|
+
// many turns those waves take is the runner's business, so each case names
|
|
92
|
+
// the state it needs instead of counting them.
|
|
93
|
+
await view.waitFor(() => settled(view), description);
|
|
94
|
+
return view;
|
|
80
95
|
};
|
|
81
96
|
|
|
82
97
|
describe("QuarantinePanel folder names", () => {
|
|
83
98
|
it("waits for the account's delimiter rather than cutting on a guess", async () => {
|
|
84
|
-
|
|
99
|
+
// The mailbox read never resolves here, so settled means every read that
|
|
100
|
+
// can land has: the entries are in hand and only the delimiter is missing.
|
|
101
|
+
const view = await mount(
|
|
102
|
+
() => new Promise(() => {}),
|
|
103
|
+
(pane) =>
|
|
104
|
+
quarantineLanded(pane) && (http?.to("/mailboxes").length ?? 0) > 0,
|
|
105
|
+
"the quarantine list to land while the mailbox read hangs",
|
|
106
|
+
);
|
|
85
107
|
|
|
86
108
|
assert.match(view.text(), /Checking for messages set aside/);
|
|
87
109
|
assert.doesNotMatch(
|
|
@@ -91,7 +113,11 @@ describe("QuarantinePanel folder names", () => {
|
|
|
91
113
|
});
|
|
92
114
|
|
|
93
115
|
it("names the folder by its leaf once the delimiter has arrived", async () => {
|
|
94
|
-
const view = await mount(
|
|
116
|
+
const view = await mount(
|
|
117
|
+
() => MAILBOXES,
|
|
118
|
+
(pane) => /Q3/.test(pane.text()),
|
|
119
|
+
"the folder leaf to be named",
|
|
120
|
+
);
|
|
95
121
|
|
|
96
122
|
assert.match(view.text(), /Q3/);
|
|
97
123
|
assert.doesNotMatch(
|
|
@@ -101,7 +127,11 @@ describe("QuarantinePanel folder names", () => {
|
|
|
101
127
|
});
|
|
102
128
|
|
|
103
129
|
it("reports a failed mailbox read instead of naming folders on a guess", async () => {
|
|
104
|
-
const view = await mount(
|
|
130
|
+
const view = await mount(
|
|
131
|
+
() => httpError(500),
|
|
132
|
+
(pane) => /role="alert"/.test(pane.html()),
|
|
133
|
+
"the failed mailbox read to be reported",
|
|
134
|
+
);
|
|
105
135
|
|
|
106
136
|
assert.match(view.html(), /role="alert"/);
|
|
107
137
|
assert.doesNotMatch(
|
package/src/lib/config-import.ts
CHANGED
|
@@ -403,11 +403,6 @@ export const pendingFolders = (
|
|
|
403
403
|
return folders;
|
|
404
404
|
};
|
|
405
405
|
|
|
406
|
-
export const anchorWarnings = (
|
|
407
|
-
report: RemitImapConfigImportReport,
|
|
408
|
-
): ApiError[] =>
|
|
409
|
-
report.warnings.filter((warning) => warning.code !== FOLDER_PENDING_CODE);
|
|
410
|
-
|
|
411
406
|
/* ------------------------------------------------------------------ */
|
|
412
407
|
/* Reading a file off disk */
|
|
413
408
|
/* ------------------------------------------------------------------ */
|
package/src/lib/i18n.ts
CHANGED
package/src/test-support/dom.ts
CHANGED
|
@@ -39,6 +39,16 @@ export interface DomHarness {
|
|
|
39
39
|
flush: () => Promise<void>;
|
|
40
40
|
/** Let real timers fire — some flows stage their steps on a delay. */
|
|
41
41
|
wait: (ms: number) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Pump the render until `predicate` holds, then return with the DOM current.
|
|
44
|
+
* A drain counted in ticks is a guess about how many awaits a read chain
|
|
45
|
+
* takes, and a loaded runner always finds one more; a condition is not.
|
|
46
|
+
*/
|
|
47
|
+
waitFor: (
|
|
48
|
+
predicate: () => boolean,
|
|
49
|
+
description?: string,
|
|
50
|
+
timeoutMs?: number,
|
|
51
|
+
) => Promise<void>;
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
export interface DomOptions {
|
|
@@ -186,6 +196,24 @@ export const createDomHarness = (options: DomOptions = {}): DomHarness => {
|
|
|
186
196
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
197
|
});
|
|
188
198
|
},
|
|
199
|
+
waitFor: async (
|
|
200
|
+
predicate,
|
|
201
|
+
description = "the render to settle",
|
|
202
|
+
timeoutMs = 2_000,
|
|
203
|
+
) => {
|
|
204
|
+
const deadline = Date.now() + timeoutMs;
|
|
205
|
+
for (;;) {
|
|
206
|
+
await harness.flush();
|
|
207
|
+
if (predicate()) return;
|
|
208
|
+
if (Date.now() > deadline) {
|
|
209
|
+
const shown = harness.text().trim() || "(nothing rendered)";
|
|
210
|
+
throw new Error(
|
|
211
|
+
`waited ${timeoutMs}ms for ${description}; the render shows: ${shown.slice(0, 400)}`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
await harness.wait(1);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
189
217
|
flush: async () => {
|
|
190
218
|
// Enough turns for a chain of awaits — form validation, then the
|
|
191
219
|
// mutation, then its `onSuccess` — to settle.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|