@nocobase/app-template-default 1.0.0-beta.85 → 1.0.0-beta.87
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/.agents/rules/shell.md +2 -0
- package/client/extensions/nocobase-file-component-ui/components/previewers/file-preview-content.tsx +1 -1
- package/client/layouts/components/header-actions.tsx +0 -2
- package/client/layouts/components/user-menu.tsx +6 -13
- package/client/locales/system/en-US.ts +1 -1
- package/client/locales/system/zh-CN.ts +1 -1
- package/client/routing/standalone-page-layout.tsx +0 -5
- package/client/theme/theme-settings.tsx +60 -162
- package/config.example.yml +50 -31
- package/package.json +31 -31
- package/server/config/auth.ts +1 -1
- package/server/config/index.ts +3 -0
- package/server/config/mailers.ts +11 -0
- package/tests/client/client-shell.test.tsx +6 -2
- package/tests/client/client-theme.test.tsx +118 -39
- package/tests/client/header-hover.test.tsx +33 -5
- package/tests/client/standalone-page-layout.test.tsx +45 -0
- package/tests/client/user-menu-sign-out.test.tsx +38 -14
- package/tests/client/user-menu.test.tsx +65 -0
- package/tests/fixtures/theme-tokens.tsx +13 -1
- package/tests/server/auth-email-config.test.ts +51 -17
- package/tests/server/concurrency.test.ts +65 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { expect, it } from 'vitest';
|
|
1
|
+
import { expect, it, vi } from 'vitest';
|
|
2
|
+
import { ConcurrentAbortError } from '@nocobase/concurrency';
|
|
2
3
|
import { createAppTest } from '@nocobase/app-testing/server';
|
|
3
4
|
import {
|
|
4
5
|
concurrencyServiceToken,
|
|
@@ -23,6 +24,55 @@ const test = createAppTest({
|
|
|
23
24
|
},
|
|
24
25
|
});
|
|
25
26
|
|
|
27
|
+
test('explicit ttl keeps local Lock and Executor mutually exclusive until actual release', async ({
|
|
28
|
+
app,
|
|
29
|
+
}) => {
|
|
30
|
+
const manager = await app.container
|
|
31
|
+
.resolve(concurrencyServiceToken)
|
|
32
|
+
.getManager();
|
|
33
|
+
const lock = await manager.createLock('concurrency-test:ttl', { ttl: 1 });
|
|
34
|
+
const executor = await manager.createSynchronous('concurrency-test:ttl', {
|
|
35
|
+
ttl: 2,
|
|
36
|
+
});
|
|
37
|
+
vi.useFakeTimers();
|
|
38
|
+
try {
|
|
39
|
+
const held = await lock.lock();
|
|
40
|
+
let entered = false;
|
|
41
|
+
const done = Promise.withResolvers<string>();
|
|
42
|
+
const running = executor.run(() => {
|
|
43
|
+
entered = true;
|
|
44
|
+
return done.promise;
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
48
|
+
expect(entered).toBe(false);
|
|
49
|
+
await held.release();
|
|
50
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
51
|
+
expect(entered).toBe(true);
|
|
52
|
+
let successorEntered = false;
|
|
53
|
+
const next = lock.lock().then((lease) => {
|
|
54
|
+
successorEntered = true;
|
|
55
|
+
return lease;
|
|
56
|
+
});
|
|
57
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
58
|
+
expect(successorEntered).toBe(false);
|
|
59
|
+
done.resolve('actual');
|
|
60
|
+
expect(await running).toBe('actual');
|
|
61
|
+
const successor = await next;
|
|
62
|
+
await held.release();
|
|
63
|
+
const attempt = executor.tryRun(() => 'wrong', 10);
|
|
64
|
+
await vi.advanceTimersByTimeAsync(10);
|
|
65
|
+
expect(await attempt).toEqual({ acquired: false });
|
|
66
|
+
await successor.release();
|
|
67
|
+
} finally {
|
|
68
|
+
done.resolve('actual');
|
|
69
|
+
await running;
|
|
70
|
+
await held.release();
|
|
71
|
+
}
|
|
72
|
+
} finally {
|
|
73
|
+
vi.useRealTimers();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
26
76
|
test('the real default composition supplies one memory lock domain to two business consumers', async ({
|
|
27
77
|
app,
|
|
28
78
|
}) => {
|
|
@@ -50,6 +100,20 @@ test('the real default composition supplies one memory lock domain to two busine
|
|
|
50
100
|
acquired: true,
|
|
51
101
|
value: 'done',
|
|
52
102
|
});
|
|
103
|
+
const cancellation = new AbortController();
|
|
104
|
+
const held = await lock.lock();
|
|
105
|
+
const cancelled = lock
|
|
106
|
+
.lock({ signal: cancellation.signal })
|
|
107
|
+
.catch((error: unknown) => error);
|
|
108
|
+
cancellation.abort();
|
|
109
|
+
expect(await cancelled).toBeInstanceOf(ConcurrentAbortError);
|
|
110
|
+
await held.release();
|
|
111
|
+
const active = await lock.lock();
|
|
112
|
+
const interrupted = lock.lock().catch((error: unknown) => error);
|
|
113
|
+
await app.shutdown();
|
|
114
|
+
expect(await interrupted).toBeInstanceOf(ConcurrentAbortError);
|
|
115
|
+
await active.release();
|
|
116
|
+
await expect(lock.lock()).rejects.toBeInstanceOf(ConcurrentAbortError);
|
|
53
117
|
});
|
|
54
118
|
|
|
55
119
|
it('declares only the default selector environment variable and no public configuration', () => {
|