@gravito/signal 3.0.4 → 3.1.2
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/README.md +26 -15
- package/dist/Mailable.d.ts +453 -0
- package/dist/OrbitSignal.d.ts +267 -0
- package/{src/Queueable.ts → dist/Queueable.d.ts} +1 -1
- package/{src/TypedMailable.ts → dist/TypedMailable.d.ts} +12 -13
- package/dist/dev/DevMailbox.d.ts +79 -0
- package/dist/dev/DevServer.d.ts +39 -0
- package/dist/dev/storage/FileMailboxStorage.d.ts +16 -0
- package/dist/dev/storage/MailboxStorage.d.ts +14 -0
- package/dist/dev/storage/MemoryMailboxStorage.d.ts +13 -0
- package/dist/dev/ui/mailbox.d.ts +11 -0
- package/dist/dev/ui/preview.d.ts +11 -0
- package/dist/dev/ui/shared.d.ts +15 -0
- package/dist/errors.d.ts +58 -0
- package/{src/events.ts → dist/events.d.ts} +20 -29
- package/dist/{lib-HJTRWKU5.mjs → index.cjs} +4687 -10983
- package/dist/index.d.ts +303 -1874
- package/dist/index.js +317 -45939
- package/dist/index.mjs +59748 -27
- package/dist/renderers/HtmlRenderer.d.ts +34 -0
- package/dist/renderers/MjmlRenderer.d.ts +41 -0
- package/dist/renderers/ReactMjmlRenderer.d.ts +38 -0
- package/dist/renderers/ReactRenderer.d.ts +46 -0
- package/{src/renderers/Renderer.ts → dist/renderers/Renderer.d.ts} +23 -24
- package/dist/renderers/TemplateRenderer.d.ts +55 -0
- package/dist/renderers/VueMjmlRenderer.d.ts +39 -0
- package/dist/renderers/VueRenderer.d.ts +47 -0
- package/dist/renderers/mjml-templates.d.ts +11 -0
- package/dist/transports/BaseTransport.d.ts +111 -0
- package/dist/transports/LogTransport.d.ts +42 -0
- package/dist/transports/MemoryTransport.d.ts +51 -0
- package/dist/transports/SesTransport.d.ts +79 -0
- package/dist/transports/SmtpTransport.d.ts +124 -0
- package/dist/transports/Transport.d.ts +44 -0
- package/dist/types.d.ts +294 -0
- package/{src/utils/html.ts → dist/utils/html.d.ts} +1 -15
- package/dist/webhooks/SendGridWebhookDriver.d.ts +45 -0
- package/dist/webhooks/SesWebhookDriver.d.ts +23 -0
- package/package.json +20 -8
- package/CHANGELOG.md +0 -74
- package/dist/MjmlRenderer-IUH663FT.mjs +0 -8
- package/dist/ReactMjmlRenderer-C3P5YO5L.mjs +0 -8
- package/dist/ReactRenderer-24SQ4KRU.mjs +0 -27
- package/dist/ReactRenderer-2JFLRVST.mjs +0 -45
- package/dist/ReactRenderer-CMCAOEPH.mjs +0 -28
- package/dist/ReactRenderer-KYNA4WKE.mjs +0 -28
- package/dist/ReactRenderer-LYEOSYFS.mjs +0 -28
- package/dist/ReactRenderer-V54CUUEI.mjs +0 -45
- package/dist/VueMjmlRenderer-4F4CXHDB.mjs +0 -8
- package/dist/VueMjmlRenderer-5WZR4CQG.mjs +0 -8
- package/dist/VueMjmlRenderer-U5YMWI44.mjs +0 -8
- package/dist/VueRenderer-3YBRQXME.mjs +0 -48
- package/dist/VueRenderer-46JGXTJ2.mjs +0 -48
- package/dist/VueRenderer-5KWD4R3C.mjs +0 -48
- package/dist/VueRenderer-C23U4O5E.mjs +0 -48
- package/dist/VueRenderer-DWTCD2RF.mjs +0 -31
- package/dist/VueRenderer-IIR5SYTM.mjs +0 -31
- package/dist/VueRenderer-LEVDFLHP.mjs +0 -31
- package/dist/VueRenderer-RNHSCCRI.mjs +0 -48
- package/dist/VueRenderer-SUP66ISX.mjs +0 -29
- package/dist/chunk-3WOR3XSL.mjs +0 -82
- package/dist/chunk-DBFIVHHG.mjs +0 -79
- package/dist/chunk-EBO3CZXG.mjs +0 -15
- package/dist/chunk-HEBXNMVQ.mjs +0 -48
- package/dist/chunk-KB7IDDBT.mjs +0 -82
- package/dist/chunk-LZL5UUPC.mjs +0 -82
- package/dist/chunk-W6LXIJKK.mjs +0 -57
- package/dist/chunk-XBIVBJS2.mjs +0 -8
- package/dist/index.d.mts +0 -2115
- package/dist/server-renderer-4IM3P5XZ.mjs +0 -37183
- package/dist/server-renderer-4W4FI7YG.mjs +0 -37269
- package/dist/server-renderer-7KWFSTPV.mjs +0 -37193
- package/dist/server-renderer-S5FPSTJ2.mjs +0 -37183
- package/dist/server-renderer-X5LUFVWT.mjs +0 -37193
- package/doc/OPTIMIZATION_PLAN.md +0 -496
- package/scripts/check-coverage.ts +0 -64
- package/src/Mailable.ts +0 -674
- package/src/OrbitSignal.ts +0 -451
- package/src/dev/DevMailbox.ts +0 -146
- package/src/dev/DevServer.ts +0 -192
- package/src/dev/storage/FileMailboxStorage.ts +0 -66
- package/src/dev/storage/MailboxStorage.ts +0 -15
- package/src/dev/storage/MemoryMailboxStorage.ts +0 -36
- package/src/dev/ui/mailbox.ts +0 -77
- package/src/dev/ui/preview.ts +0 -103
- package/src/dev/ui/shared.ts +0 -60
- package/src/errors.ts +0 -69
- package/src/index.ts +0 -41
- package/src/renderers/HtmlRenderer.ts +0 -41
- package/src/renderers/MjmlRenderer.ts +0 -73
- package/src/renderers/ReactMjmlRenderer.ts +0 -94
- package/src/renderers/ReactRenderer.ts +0 -66
- package/src/renderers/TemplateRenderer.ts +0 -84
- package/src/renderers/VueMjmlRenderer.ts +0 -99
- package/src/renderers/VueRenderer.ts +0 -71
- package/src/renderers/mjml-templates.ts +0 -50
- package/src/transports/BaseTransport.ts +0 -148
- package/src/transports/LogTransport.ts +0 -55
- package/src/transports/MemoryTransport.ts +0 -55
- package/src/transports/SesTransport.ts +0 -129
- package/src/transports/SmtpTransport.ts +0 -184
- package/src/transports/Transport.ts +0 -45
- package/src/types.ts +0 -309
- package/src/webhooks/SendGridWebhookDriver.ts +0 -80
- package/src/webhooks/SesWebhookDriver.ts +0 -44
- package/tests/DevMailbox.test.ts +0 -54
- package/tests/FileMailboxStorage.test.ts +0 -56
- package/tests/MjmlLayout.test.ts +0 -28
- package/tests/MjmlRenderer.test.ts +0 -53
- package/tests/OrbitSignalWebhook.test.ts +0 -56
- package/tests/ReactMjmlRenderer.test.ts +0 -33
- package/tests/SendGridWebhookDriver.test.ts +0 -69
- package/tests/SesWebhookDriver.test.ts +0 -46
- package/tests/VueMjmlRenderer.test.ts +0 -35
- package/tests/dev-server.test.ts +0 -66
- package/tests/log-transport.test.ts +0 -21
- package/tests/mailable-extra.test.ts +0 -68
- package/tests/mailable.test.ts +0 -77
- package/tests/orbit-signal.test.ts +0 -43
- package/tests/renderers.test.ts +0 -58
- package/tests/template-renderer.test.ts +0 -24
- package/tests/transports.test.ts +0 -52
- package/tests/ui.test.ts +0 -37
- package/tsconfig.json +0 -14
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from 'bun:test'
|
|
2
|
-
import { ReactMjmlRenderer } from '../src/renderers/ReactMjmlRenderer'
|
|
3
|
-
|
|
4
|
-
describe('ReactMjmlRenderer', () => {
|
|
5
|
-
it('should render React component to MJML then to HTML', async () => {
|
|
6
|
-
const mockComponent = {}
|
|
7
|
-
const mockElement = { type: 'mjml' }
|
|
8
|
-
const mockMjml = '<mjml><mj-body>Hello</mj-body></mjml>'
|
|
9
|
-
const mockHtml = '<html><body>Hello</body></html>'
|
|
10
|
-
|
|
11
|
-
const createElement = mock(() => mockElement)
|
|
12
|
-
const renderToStaticMarkup = mock(() => mockMjml)
|
|
13
|
-
const mjml2html = mock(() => ({ html: mockHtml, errors: [] }))
|
|
14
|
-
|
|
15
|
-
const renderer = new ReactMjmlRenderer(
|
|
16
|
-
mockComponent,
|
|
17
|
-
{ name: 'World' },
|
|
18
|
-
{},
|
|
19
|
-
{
|
|
20
|
-
createElement,
|
|
21
|
-
renderToStaticMarkup,
|
|
22
|
-
mjml2html,
|
|
23
|
-
}
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
const result = await renderer.render({ extra: 'data' })
|
|
27
|
-
|
|
28
|
-
expect(createElement).toHaveBeenCalledWith(mockComponent, { name: 'World', extra: 'data' })
|
|
29
|
-
expect(renderToStaticMarkup).toHaveBeenCalledWith(mockElement)
|
|
30
|
-
expect(mjml2html).toHaveBeenCalledWith(mockMjml, expect.any(Object))
|
|
31
|
-
expect(result.html).toBe(mockHtml)
|
|
32
|
-
})
|
|
33
|
-
})
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import type { GravitoContext } from '@gravito/core'
|
|
3
|
-
import { SendGridWebhookDriver } from '../src/webhooks/SendGridWebhookDriver'
|
|
4
|
-
|
|
5
|
-
describe('SendGridWebhookDriver', () => {
|
|
6
|
-
it('should parse batch events from request body', async () => {
|
|
7
|
-
const driver = new SendGridWebhookDriver()
|
|
8
|
-
|
|
9
|
-
const mockEvents = [
|
|
10
|
-
{ event: 'delivered', email: 'user1@example.com', timestamp: 123456789 },
|
|
11
|
-
{ event: 'opened', email: 'user1@example.com', timestamp: 123456790 },
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
const mockCtx = {
|
|
15
|
-
req: {
|
|
16
|
-
json: async () => mockEvents,
|
|
17
|
-
header: () => undefined,
|
|
18
|
-
},
|
|
19
|
-
} as unknown as GravitoContext
|
|
20
|
-
|
|
21
|
-
const result = await driver.handle(mockCtx)
|
|
22
|
-
|
|
23
|
-
expect(result).toHaveLength(2)
|
|
24
|
-
expect(result?.[0].event).toBe('delivered')
|
|
25
|
-
expect(result?.[1].event).toBe('opened')
|
|
26
|
-
expect(result?.[0].payload.email).toBe('user1@example.com')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should handle single event body', async () => {
|
|
30
|
-
const driver = new SendGridWebhookDriver()
|
|
31
|
-
const mockEvent = { event: 'bounced', email: 'bad@example.com' }
|
|
32
|
-
|
|
33
|
-
const mockCtx = {
|
|
34
|
-
req: {
|
|
35
|
-
json: async () => mockEvent,
|
|
36
|
-
header: () => undefined,
|
|
37
|
-
},
|
|
38
|
-
} as unknown as GravitoContext
|
|
39
|
-
|
|
40
|
-
const result = await driver.handle(mockCtx)
|
|
41
|
-
expect(result).toHaveLength(1)
|
|
42
|
-
expect(result?.[0].event).toBe('bounced')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should return null for empty body', async () => {
|
|
46
|
-
const driver = new SendGridWebhookDriver()
|
|
47
|
-
const mockCtx = {
|
|
48
|
-
req: {
|
|
49
|
-
json: async () => [],
|
|
50
|
-
header: () => undefined,
|
|
51
|
-
},
|
|
52
|
-
} as unknown as GravitoContext
|
|
53
|
-
|
|
54
|
-
const result = await driver.handle(mockCtx)
|
|
55
|
-
expect(result).toBeNull()
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('should throw if public key is set but signature is missing', async () => {
|
|
59
|
-
const driver = new SendGridWebhookDriver({ publicKey: 'some-key' })
|
|
60
|
-
const mockCtx = {
|
|
61
|
-
req: {
|
|
62
|
-
json: async () => [{ event: 'test' }],
|
|
63
|
-
header: (_name: string) => undefined,
|
|
64
|
-
},
|
|
65
|
-
} as unknown as GravitoContext
|
|
66
|
-
|
|
67
|
-
expect(driver.handle(mockCtx)).rejects.toThrow('Missing SendGrid signature headers')
|
|
68
|
-
})
|
|
69
|
-
})
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import type { GravitoContext } from '@gravito/core'
|
|
3
|
-
import { SesWebhookDriver } from '../src/webhooks/SesWebhookDriver'
|
|
4
|
-
|
|
5
|
-
describe('SesWebhookDriver', () => {
|
|
6
|
-
it('should parse SNS notification events', async () => {
|
|
7
|
-
const driver = new SesWebhookDriver()
|
|
8
|
-
|
|
9
|
-
const mockSnsBody = {
|
|
10
|
-
Type: 'Notification',
|
|
11
|
-
Message: JSON.stringify({
|
|
12
|
-
notificationType: 'Bounce',
|
|
13
|
-
bounce: { bounceType: 'Permanent', bounceSubType: 'General' },
|
|
14
|
-
}),
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const mockCtx = {
|
|
18
|
-
req: {
|
|
19
|
-
json: async () => mockSnsBody,
|
|
20
|
-
},
|
|
21
|
-
} as unknown as GravitoContext
|
|
22
|
-
|
|
23
|
-
const result = await driver.handle(mockCtx)
|
|
24
|
-
|
|
25
|
-
expect(result).toHaveLength(1)
|
|
26
|
-
expect(result?.[0].event).toBe('bounce')
|
|
27
|
-
expect(result?.[0].payload.bounce.bounceType).toBe('Permanent')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should handle subscription confirmation', async () => {
|
|
31
|
-
const driver = new SesWebhookDriver()
|
|
32
|
-
const mockSnsBody = {
|
|
33
|
-
Type: 'SubscriptionConfirmation',
|
|
34
|
-
SubscribeURL: 'https://sns.aws.com/confirm',
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const mockCtx = {
|
|
38
|
-
req: {
|
|
39
|
-
json: async () => mockSnsBody,
|
|
40
|
-
},
|
|
41
|
-
} as unknown as GravitoContext
|
|
42
|
-
|
|
43
|
-
const result = await driver.handle(mockCtx)
|
|
44
|
-
expect(result?.[0].event).toBe('sns:subscription')
|
|
45
|
-
})
|
|
46
|
-
})
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from 'bun:test'
|
|
2
|
-
import { VueMjmlRenderer } from '../src/renderers/VueMjmlRenderer'
|
|
3
|
-
|
|
4
|
-
describe('VueMjmlRenderer', () => {
|
|
5
|
-
it('should render Vue component to MJML then to HTML', async () => {
|
|
6
|
-
const mockComponent = { name: 'Test' }
|
|
7
|
-
const mockApp = { _context: {} }
|
|
8
|
-
const mockMjml = '<mjml><mj-body>Vue MJML</mj-body></mjml>'
|
|
9
|
-
const mockHtml = '<html><body>Vue MJML</body></html>'
|
|
10
|
-
|
|
11
|
-
const createSSRApp = mock(() => mockApp)
|
|
12
|
-
const h = mock(() => ({}))
|
|
13
|
-
const renderToString = mock(async () => mockMjml)
|
|
14
|
-
const mjml2html = mock(() => ({ html: mockHtml, errors: [] }))
|
|
15
|
-
|
|
16
|
-
const renderer = new VueMjmlRenderer(
|
|
17
|
-
mockComponent,
|
|
18
|
-
{ name: 'World' },
|
|
19
|
-
{},
|
|
20
|
-
{
|
|
21
|
-
createSSRApp,
|
|
22
|
-
h,
|
|
23
|
-
renderToString,
|
|
24
|
-
mjml2html,
|
|
25
|
-
}
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const result = await renderer.render({ extra: 'data' })
|
|
29
|
-
|
|
30
|
-
expect(createSSRApp).toHaveBeenCalled()
|
|
31
|
-
expect(renderToString).toHaveBeenCalledWith(mockApp)
|
|
32
|
-
expect(mjml2html).toHaveBeenCalledWith(mockMjml, expect.any(Object))
|
|
33
|
-
expect(result.html).toBe(mockHtml)
|
|
34
|
-
})
|
|
35
|
-
})
|
package/tests/dev-server.test.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from 'bun:test'
|
|
2
|
-
import { DevMailbox } from '../src/dev/DevMailbox'
|
|
3
|
-
import { DevServer } from '../src/dev/DevServer'
|
|
4
|
-
|
|
5
|
-
const createCore = () => {
|
|
6
|
-
const routes = new Map<string, (ctx: any) => any>()
|
|
7
|
-
const deletes = new Map<string, (ctx: any) => any>()
|
|
8
|
-
const core = {
|
|
9
|
-
router: {
|
|
10
|
-
get: (path: string, handler: (ctx: any) => any) => routes.set(path, handler),
|
|
11
|
-
delete: (path: string, handler: (ctx: any) => any) => deletes.set(path, handler),
|
|
12
|
-
},
|
|
13
|
-
logger: { info: mock(() => {}) },
|
|
14
|
-
routes,
|
|
15
|
-
deletes,
|
|
16
|
-
}
|
|
17
|
-
return core
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe('DevServer', () => {
|
|
21
|
-
it('registers mailbox routes and serves entries', async () => {
|
|
22
|
-
const mailbox = new DevMailbox()
|
|
23
|
-
const entry = await mailbox.add({
|
|
24
|
-
from: { address: 'from@example.com' },
|
|
25
|
-
to: [{ address: 'to@example.com' }],
|
|
26
|
-
subject: 'Hello',
|
|
27
|
-
html: '<p>Hi</p>',
|
|
28
|
-
priority: 'normal',
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const core = createCore()
|
|
32
|
-
const server = new DevServer(mailbox, '/__mail/', { allowInProduction: true })
|
|
33
|
-
server.register(core as any)
|
|
34
|
-
|
|
35
|
-
const list = await core.routes.get('/__mail')?.({
|
|
36
|
-
html: (body: string) => body,
|
|
37
|
-
header: () => {},
|
|
38
|
-
})
|
|
39
|
-
expect(list).toContain('Hello')
|
|
40
|
-
|
|
41
|
-
const preview = await core.routes.get('/__mail/:id')?.({
|
|
42
|
-
req: { param: () => entry.id },
|
|
43
|
-
html: (body: string) => body,
|
|
44
|
-
text: () => '',
|
|
45
|
-
header: () => {},
|
|
46
|
-
})
|
|
47
|
-
expect(preview).toContain('Email Preview')
|
|
48
|
-
|
|
49
|
-
const raw = await core.routes.get('/__mail/:id/raw')?.({
|
|
50
|
-
req: { param: () => entry.id },
|
|
51
|
-
json: (body: unknown) => body,
|
|
52
|
-
})
|
|
53
|
-
expect((raw as any).id).toBe(entry.id)
|
|
54
|
-
|
|
55
|
-
const deleted = await core.deletes.get('/__mail/:id')?.({
|
|
56
|
-
req: { param: () => entry.id },
|
|
57
|
-
json: (body: unknown) => body,
|
|
58
|
-
})
|
|
59
|
-
expect((deleted as any).success).toBe(true)
|
|
60
|
-
|
|
61
|
-
const cleared = await core.deletes.get('/__mail')?.({
|
|
62
|
-
json: (body: unknown) => body,
|
|
63
|
-
})
|
|
64
|
-
expect((cleared as any).success).toBe(true)
|
|
65
|
-
})
|
|
66
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from 'bun:test'
|
|
2
|
-
import { LogTransport } from '../src/transports/LogTransport'
|
|
3
|
-
|
|
4
|
-
describe('LogTransport', () => {
|
|
5
|
-
it('logs formatted output', async () => {
|
|
6
|
-
const originalLog = console.log
|
|
7
|
-
console.log = mock(() => {})
|
|
8
|
-
|
|
9
|
-
const transport = new LogTransport()
|
|
10
|
-
await transport.send({
|
|
11
|
-
from: { address: 'from@example.com' },
|
|
12
|
-
to: [{ address: 'to@example.com' }],
|
|
13
|
-
subject: 'Hello',
|
|
14
|
-
html: '<p>Hi</p>',
|
|
15
|
-
priority: 'normal',
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
expect((console.log as any).mock.calls.length).toBeGreaterThan(0)
|
|
19
|
-
console.log = originalLog
|
|
20
|
-
})
|
|
21
|
-
})
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { Mailable } from '../src/Mailable'
|
|
3
|
-
|
|
4
|
-
class TestMailable extends Mailable {
|
|
5
|
-
build() {
|
|
6
|
-
return this
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe('Mailable Extras', () => {
|
|
11
|
-
it('should handle attachments', () => {
|
|
12
|
-
const mail = new TestMailable()
|
|
13
|
-
mail.attach({ filename: 'test.txt', content: 'hello' })
|
|
14
|
-
expect((mail as any).envelope.attachments).toHaveLength(1)
|
|
15
|
-
expect((mail as any).envelope.attachments[0].filename).toBe('test.txt')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('should set view logic', () => {
|
|
19
|
-
const mail = new TestMailable()
|
|
20
|
-
mail.view('emails/welcome', { user: 'Carl' })
|
|
21
|
-
expect((mail as any).renderer).toBeDefined()
|
|
22
|
-
expect((mail as any).renderData.user).toBe('Carl')
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('should handle queue options', () => {
|
|
26
|
-
const mail = new TestMailable()
|
|
27
|
-
mail.onQueue('low').onConnection('redis').delay(60).withPriority('high')
|
|
28
|
-
|
|
29
|
-
expect(mail.queueName).toBe('low')
|
|
30
|
-
expect(mail.connectionName).toBe('redis')
|
|
31
|
-
expect(mail.delaySeconds).toBe(60)
|
|
32
|
-
expect(mail.priority).toBe('high')
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('should handle locale', () => {
|
|
36
|
-
const mail = new TestMailable()
|
|
37
|
-
mail.locale('zh-TW')
|
|
38
|
-
expect((mail as any).currentLocale).toBe('zh-TW')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should support i18n helper', () => {
|
|
42
|
-
const mail = new TestMailable()
|
|
43
|
-
mail.setTranslator((key) => key.toUpperCase())
|
|
44
|
-
|
|
45
|
-
expect(mail.t('hello')).toBe('HELLO')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('should safely fail queue() if app not available', async () => {
|
|
49
|
-
const mail = new TestMailable()
|
|
50
|
-
// We are not mocking @gravito/core, so it should hit the catch block and log warning
|
|
51
|
-
// We just ensure it doesn't throw
|
|
52
|
-
await expect(mail.queue()).resolves.toBeUndefined()
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('should support React renderer builder', async () => {
|
|
56
|
-
const mail = new TestMailable()
|
|
57
|
-
// Mock import mechanics? Hard to test dynamic import in this unit test without mocking module system.
|
|
58
|
-
// Instead we test the builder method sets the resolver.
|
|
59
|
-
mail.react('MyComponent', { prop: 1 })
|
|
60
|
-
expect((mail as any).rendererResolver).toBeDefined()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('should support Vue renderer builder', async () => {
|
|
64
|
-
const mail = new TestMailable()
|
|
65
|
-
mail.vue('MyComponent', { prop: 1 })
|
|
66
|
-
expect((mail as any).rendererResolver).toBeDefined()
|
|
67
|
-
})
|
|
68
|
-
})
|
package/tests/mailable.test.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { Mailable } from '../src/Mailable'
|
|
3
|
-
import { OrbitSignal } from '../src/OrbitSignal'
|
|
4
|
-
import { LogTransport } from '../src/transports/LogTransport'
|
|
5
|
-
import type { Message, Transport } from '../src/transports/Transport'
|
|
6
|
-
|
|
7
|
-
// Mock Transport
|
|
8
|
-
class MockTransport implements Transport {
|
|
9
|
-
public sentMessages: Message[] = []
|
|
10
|
-
async send(message: Message): Promise<void> {
|
|
11
|
-
this.sentMessages.push(message)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Example Mailable
|
|
16
|
-
class WelcomeMail extends Mailable {
|
|
17
|
-
constructor(private name: string) {
|
|
18
|
-
super()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
build() {
|
|
22
|
-
return this.subject('Welcome!').html(`<h1>Welcome, ${this.name}</h1>`)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('OrbitSignal Core', () => {
|
|
27
|
-
it('should send a simple email via transport', async () => {
|
|
28
|
-
const transport = new MockTransport()
|
|
29
|
-
|
|
30
|
-
const mailer = new OrbitSignal({
|
|
31
|
-
from: { name: 'System', address: 'system@example.com' },
|
|
32
|
-
transport: transport,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const mail = new WelcomeMail('Carl').to('carl@example.com')
|
|
36
|
-
|
|
37
|
-
await mailer.send(mail)
|
|
38
|
-
|
|
39
|
-
expect(transport.sentMessages.length).toBe(1)
|
|
40
|
-
const msg = transport.sentMessages[0]
|
|
41
|
-
|
|
42
|
-
expect(msg.to[0].address).toBe('carl@example.com')
|
|
43
|
-
expect(msg.subject).toBe('Welcome!')
|
|
44
|
-
expect(msg.html).toBe('<h1>Welcome, Carl</h1>')
|
|
45
|
-
expect(msg.from.address).toBe('system@example.com')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('should handle fluent API correctly', async () => {
|
|
49
|
-
const mail = new WelcomeMail('Carl')
|
|
50
|
-
.to(['a@b.com', { address: 'c@d.com', name: 'C' }])
|
|
51
|
-
.cc('cc@example.com')
|
|
52
|
-
.emailPriority('high')
|
|
53
|
-
|
|
54
|
-
const config = {
|
|
55
|
-
from: { address: 'default@example.com' },
|
|
56
|
-
transport: new LogTransport(),
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const envelope = await mail.buildEnvelope(config)
|
|
60
|
-
|
|
61
|
-
expect(envelope.to).toHaveLength(2)
|
|
62
|
-
expect(envelope.cc).toHaveLength(1)
|
|
63
|
-
expect(envelope.priority).toBe('high')
|
|
64
|
-
expect(envelope.from?.address).toBe('default@example.com')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should throw error if renderer is missing', async () => {
|
|
68
|
-
class EmptyMail extends Mailable {
|
|
69
|
-
build() {
|
|
70
|
-
return this
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const mail = new EmptyMail()
|
|
75
|
-
expect(mail.renderContent()).rejects.toThrow('No content renderer')
|
|
76
|
-
})
|
|
77
|
-
})
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { DevMailbox } from '../src/dev/DevMailbox'
|
|
3
|
-
import { Mailable } from '../src/Mailable'
|
|
4
|
-
import { OrbitSignal } from '../src/OrbitSignal'
|
|
5
|
-
import { MemoryTransport } from '../src/transports/MemoryTransport'
|
|
6
|
-
|
|
7
|
-
class EmptyMail extends Mailable {
|
|
8
|
-
build() {
|
|
9
|
-
return this.html('<p>Hi</p>')
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
describe('OrbitSignal', () => {
|
|
14
|
-
it('sends messages via transport and validates fields', async () => {
|
|
15
|
-
const mailboxTransport = new MemoryTransport(new DevMailbox())
|
|
16
|
-
const orbit = new OrbitSignal({ transport: mailboxTransport })
|
|
17
|
-
|
|
18
|
-
// No from address
|
|
19
|
-
await expect(orbit.send(new EmptyMail())).rejects.toThrow('Message is missing "from"')
|
|
20
|
-
|
|
21
|
-
class ValidMail extends Mailable {
|
|
22
|
-
build() {
|
|
23
|
-
return this.from('from@example.com').to('to@example.com').html('<p>Hi</p>')
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
await expect(orbit.send(new ValidMail())).resolves.toBeUndefined()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('queues immediately when no queue service is present', async () => {
|
|
31
|
-
const transport = new MemoryTransport(new DevMailbox())
|
|
32
|
-
const orbit = new OrbitSignal({ transport })
|
|
33
|
-
|
|
34
|
-
class ValidMail extends Mailable {
|
|
35
|
-
build() {
|
|
36
|
-
return this.from('from@example.com').to('to@example.com').html('<p>Hi</p>')
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Should resolve without crashing (internally falls back to send)
|
|
41
|
-
await expect(orbit.queue(new ValidMail())).resolves.toBeUndefined()
|
|
42
|
-
})
|
|
43
|
-
})
|
package/tests/renderers.test.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { Mailable } from '../src/Mailable'
|
|
3
|
-
import { LogTransport } from '../src/transports/LogTransport'
|
|
4
|
-
|
|
5
|
-
// Mock Config for Mailable
|
|
6
|
-
const mockConfig = {
|
|
7
|
-
from: { address: 'test@example.com' },
|
|
8
|
-
transport: new LogTransport(),
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// React Component
|
|
12
|
-
const ReactTestComponent = ({ name }: { name: string }) => `<h1>Hello ${name}</h1>`
|
|
13
|
-
|
|
14
|
-
const reactDeps = {
|
|
15
|
-
createElement: (component: (props: any) => string, props: any) => component(props),
|
|
16
|
-
renderToStaticMarkup: (element: string) => element,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Vue Component
|
|
20
|
-
const VueTestComponent = ({ name }: { name: string }) => `<h1>Hello ${name}</h1>`
|
|
21
|
-
|
|
22
|
-
const vueDeps = {
|
|
23
|
-
createSSRApp: ({ render }: { render: () => string }) => ({ render }),
|
|
24
|
-
h: (component: (props: any) => string, props: any) => component(props),
|
|
25
|
-
renderToString: async (app: { render: () => string }) => app.render(),
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class TestReactMail extends Mailable {
|
|
29
|
-
build() {
|
|
30
|
-
return this.react(ReactTestComponent, { name: 'World' }, reactDeps).subject('React Test')
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
class TestVueMail extends Mailable {
|
|
35
|
-
build() {
|
|
36
|
-
return this.vue(VueTestComponent, { name: 'World' }, vueDeps).subject('Vue Test')
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe('Renderers', () => {
|
|
41
|
-
it('should render React component to HTML', async () => {
|
|
42
|
-
const mail = new TestReactMail()
|
|
43
|
-
const _envelope = await mail.buildEnvelope(mockConfig)
|
|
44
|
-
const { html } = await mail.renderContent()
|
|
45
|
-
|
|
46
|
-
expect(html).toContain('<!DOCTYPE html>')
|
|
47
|
-
expect(html).toContain('<h1>Hello World</h1>')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('should render Vue component to HTML', async () => {
|
|
51
|
-
const mail = new TestVueMail()
|
|
52
|
-
const _envelope = await mail.buildEnvelope(mockConfig)
|
|
53
|
-
const { html } = await mail.renderContent()
|
|
54
|
-
|
|
55
|
-
expect(html).toContain('<!DOCTYPE html>')
|
|
56
|
-
expect(html).toContain('<h1>Hello World</h1>')
|
|
57
|
-
})
|
|
58
|
-
})
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
mock.module('@gravito/prism', () => ({
|
|
4
|
-
TemplateEngine: class {
|
|
5
|
-
constructor(private dir: string) {
|
|
6
|
-
this.dir = dir
|
|
7
|
-
}
|
|
8
|
-
render(template: string) {
|
|
9
|
-
return `<div>${this.dir}:${template}</div>`
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
|
-
}))
|
|
13
|
-
|
|
14
|
-
const { TemplateRenderer } = await import('../src/renderers/TemplateRenderer')
|
|
15
|
-
|
|
16
|
-
describe('TemplateRenderer', () => {
|
|
17
|
-
it('renders templates and strips html', async () => {
|
|
18
|
-
const renderer = new TemplateRenderer('welcome', '/views')
|
|
19
|
-
const result = await renderer.render({ name: 'Ada' })
|
|
20
|
-
|
|
21
|
-
expect(result.html).toContain('/views:welcome')
|
|
22
|
-
expect(result.text).toBe('/views:welcome')
|
|
23
|
-
})
|
|
24
|
-
})
|
package/tests/transports.test.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { DevMailbox } from '../src/dev/DevMailbox'
|
|
3
|
-
import { MemoryTransport } from '../src/transports/MemoryTransport'
|
|
4
|
-
import type { Message } from '../src/types'
|
|
5
|
-
|
|
6
|
-
describe('Transports', () => {
|
|
7
|
-
describe('MemoryTransport & DevMailbox', () => {
|
|
8
|
-
it('should store sent emails in mailbox', async () => {
|
|
9
|
-
const mailbox = new DevMailbox()
|
|
10
|
-
const transport = new MemoryTransport(mailbox)
|
|
11
|
-
|
|
12
|
-
const message: Message = {
|
|
13
|
-
from: { address: 'from@example.com' },
|
|
14
|
-
to: [{ address: 'to@example.com' }],
|
|
15
|
-
subject: 'Test Subject',
|
|
16
|
-
html: '<p>Content</p>',
|
|
17
|
-
priority: 'normal',
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
await transport.send(message)
|
|
21
|
-
|
|
22
|
-
const entries = await mailbox.list()
|
|
23
|
-
expect(entries).toHaveLength(1)
|
|
24
|
-
|
|
25
|
-
const entry = entries[0]
|
|
26
|
-
expect(entry.envelope.subject).toBe('Test Subject')
|
|
27
|
-
expect(entry.html).toBe('<p>Content</p>')
|
|
28
|
-
expect(entry.sentAt).toBeInstanceOf(Date)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should limit mailbox size', async () => {
|
|
32
|
-
const mailbox = new DevMailbox()
|
|
33
|
-
// Hack: set max entries lower for test if possible, or just add 51 entries
|
|
34
|
-
// Since maxEntries is private, we just add 51
|
|
35
|
-
const transport = new MemoryTransport(mailbox)
|
|
36
|
-
|
|
37
|
-
for (let i = 0; i < 60; i++) {
|
|
38
|
-
await transport.send({
|
|
39
|
-
from: { address: 'a@b.com' },
|
|
40
|
-
to: [{ address: 'b@c.com' }],
|
|
41
|
-
subject: `Msg ${i}`,
|
|
42
|
-
html: 'content',
|
|
43
|
-
priority: 'normal',
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
expect((await mailbox.list()).length).toBeLessThanOrEqual(50)
|
|
48
|
-
// Should be the latest ones (last one added is index 0)
|
|
49
|
-
expect((await mailbox.list())[0].envelope.subject).toBe('Msg 59')
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
})
|
package/tests/ui.test.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { getMailboxHtml } from '../src/dev/ui/mailbox'
|
|
3
|
-
import { getPreviewHtml } from '../src/dev/ui/preview'
|
|
4
|
-
import { layout } from '../src/dev/ui/shared'
|
|
5
|
-
|
|
6
|
-
describe('Dev UI', () => {
|
|
7
|
-
it('renders mailbox and preview HTML', () => {
|
|
8
|
-
const entry = {
|
|
9
|
-
id: '1',
|
|
10
|
-
envelope: {
|
|
11
|
-
from: { address: 'from@example.com' },
|
|
12
|
-
to: [{ address: 'to@example.com' }],
|
|
13
|
-
subject: 'Hello',
|
|
14
|
-
priority: 'high',
|
|
15
|
-
},
|
|
16
|
-
html: '<p>Hi</p>',
|
|
17
|
-
text: 'Hi',
|
|
18
|
-
sentAt: new Date('2024-01-01T00:00:00Z'),
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const mailbox = getMailboxHtml([entry], '/__mail')
|
|
22
|
-
expect(mailbox).toContain('Gravito Mailbox')
|
|
23
|
-
expect(mailbox).toContain('badge-high')
|
|
24
|
-
|
|
25
|
-
const preview = getPreviewHtml(entry, '/__mail')
|
|
26
|
-
expect(preview).toContain('Email Preview')
|
|
27
|
-
expect(preview).toContain('Hello')
|
|
28
|
-
|
|
29
|
-
const wrapped = layout('Title', '<div>Content</div>')
|
|
30
|
-
expect(wrapped).toContain('<title>Title')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('renders empty mailbox state', () => {
|
|
34
|
-
const html = getMailboxHtml([], '/__mail')
|
|
35
|
-
expect(html).toContain('No emails found')
|
|
36
|
-
})
|
|
37
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"baseUrl": ".",
|
|
6
|
-
"paths": {
|
|
7
|
-
"@gravito/core": ["../../packages/core/src/index.ts"],
|
|
8
|
-
"@gravito/*": ["../../packages/*/src/index.ts"]
|
|
9
|
-
},
|
|
10
|
-
"types": ["bun-types"]
|
|
11
|
-
},
|
|
12
|
-
"include": ["src/**/*"],
|
|
13
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
14
|
-
}
|