@teleporthq/teleport-plugin-next-workflows 0.43.65 → 0.43.67
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/__tests__/_helpers/bind-workflow-utils.ts +30 -0
- package/__tests__/_helpers/run-generated-middleware.ts +11 -2
- package/__tests__/admin-form-row-hydration.test.ts +55 -0
- package/__tests__/cart-clear-db-mirror.test.ts +12 -0
- package/__tests__/cart-get-total-regional-pricing.test.ts +166 -0
- package/__tests__/client-config-redaction.test.ts +53 -0
- package/__tests__/continue-on-error.test.ts +119 -0
- package/__tests__/email-locale-module.test.ts +323 -0
- package/__tests__/email-locale-senders.test.ts +268 -0
- package/__tests__/invoice-delivery-fee.test.ts +104 -0
- package/__tests__/invoice-regional-tax.test.ts +126 -0
- package/__tests__/locale-aware-redirects.test.ts +355 -0
- package/__tests__/navigation-go-to-page.test.ts +43 -0
- package/__tests__/node-handler-file-inline-map.test.ts +29 -3
- package/__tests__/payment-redirect-and-order-notification.test.ts +6 -0
- package/__tests__/paypal-webhook-order-notification.test.ts +41 -0
- package/__tests__/resolve-config-localized-templates.test.ts +262 -0
- package/__tests__/sent-email-log-module.test.ts +381 -0
- package/__tests__/sent-email-log-wiring.test.ts +267 -0
- package/dist/cjs/account-delete-route-generator.d.ts +20 -0
- package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
- package/dist/cjs/account-delete-route-generator.js +29 -26
- package/dist/cjs/account-delete-route-generator.js.map +1 -1
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +57 -11
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/auth-generator.d.ts +2 -1
- package/dist/cjs/auth-generator.d.ts.map +1 -1
- package/dist/cjs/auth-generator.js +55 -18
- package/dist/cjs/auth-generator.js.map +1 -1
- package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
- package/dist/cjs/data-api-route-generator.js +19 -0
- package/dist/cjs/data-api-route-generator.js.map +1 -1
- package/dist/cjs/email-locale/email-locale-code.d.ts +25 -0
- package/dist/cjs/email-locale/email-locale-code.d.ts.map +1 -0
- package/dist/cjs/email-locale/email-locale-code.js +228 -0
- package/dist/cjs/email-locale/email-locale-code.js.map +1 -0
- package/dist/cjs/email-locale/email-locale-scope.d.ts +46 -0
- package/dist/cjs/email-locale/email-locale-scope.d.ts.map +1 -0
- package/dist/cjs/email-locale/email-locale-scope.js +51 -0
- package/dist/cjs/email-locale/email-locale-scope.js.map +1 -0
- package/dist/cjs/email-locale/index.d.ts +11 -0
- package/dist/cjs/email-locale/index.d.ts.map +1 -0
- package/dist/cjs/email-locale/index.js +40 -0
- package/dist/cjs/email-locale/index.js.map +1 -0
- package/dist/cjs/executor-generator.d.ts +11 -1
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +49 -7
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/cjs/invoice/api-routes-code.js +55 -6
- package/dist/cjs/invoice/api-routes-code.js.map +1 -1
- package/dist/cjs/invoice/email-sender-code.d.ts.map +1 -1
- package/dist/cjs/invoice/email-sender-code.js +72 -31
- package/dist/cjs/invoice/email-sender-code.js.map +1 -1
- package/dist/cjs/invoice/index.d.ts +1 -4
- package/dist/cjs/invoice/index.d.ts.map +1 -1
- package/dist/cjs/invoice/index.js +8 -19
- package/dist/cjs/invoice/index.js.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.js +31 -8
- package/dist/cjs/invoice/invoice-html-code.js.map +1 -1
- package/dist/cjs/invoice/regional-invoice-tax-code.d.ts +25 -0
- package/dist/cjs/invoice/regional-invoice-tax-code.d.ts.map +1 -0
- package/dist/cjs/invoice/regional-invoice-tax-code.js +116 -0
- package/dist/cjs/invoice/regional-invoice-tax-code.js.map +1 -0
- package/dist/cjs/invoice/resolve-data-source.d.ts +6 -0
- package/dist/cjs/invoice/resolve-data-source.d.ts.map +1 -0
- package/dist/cjs/invoice/resolve-data-source.js +23 -0
- package/dist/cjs/invoice/resolve-data-source.js.map +1 -0
- package/dist/cjs/nodes/account/account-delete-current.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-delete-current.js +10 -3
- package/dist/cjs/nodes/account/account-delete-current.js.map +1 -1
- package/dist/cjs/nodes/account/account-login.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-login.js +3 -1
- package/dist/cjs/nodes/account/account-login.js.map +1 -1
- package/dist/cjs/nodes/account/account-logout.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-logout.js +4 -2
- package/dist/cjs/nodes/account/account-logout.js.map +1 -1
- package/dist/cjs/nodes/account/account-signup.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-signup.js +12 -2
- package/dist/cjs/nodes/account/account-signup.js.map +1 -1
- package/dist/cjs/nodes/account/account-social-login.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-social-login.js +4 -2
- package/dist/cjs/nodes/account/account-social-login.js.map +1 -1
- package/dist/cjs/nodes/cart/cart-clear.d.ts.map +1 -1
- package/dist/cjs/nodes/cart/cart-clear.js +6 -0
- package/dist/cjs/nodes/cart/cart-clear.js.map +1 -1
- package/dist/cjs/nodes/cart/cart-get-total.d.ts.map +1 -1
- package/dist/cjs/nodes/cart/cart-get-total.js +81 -5
- package/dist/cjs/nodes/cart/cart-get-total.js.map +1 -1
- package/dist/cjs/nodes/data/data-create-item.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-create-item.js +16 -0
- package/dist/cjs/nodes/data/data-create-item.js.map +1 -1
- package/dist/cjs/nodes/ecommerce/ecommerce-generate-invoice.d.ts.map +1 -1
- package/dist/cjs/nodes/ecommerce/ecommerce-generate-invoice.js +6 -0
- package/dist/cjs/nodes/ecommerce/ecommerce-generate-invoice.js.map +1 -1
- package/dist/cjs/nodes/navigation/navigation-go-to-page.d.ts.map +1 -1
- package/dist/cjs/nodes/navigation/navigation-go-to-page.js +4 -1
- package/dist/cjs/nodes/navigation/navigation-go-to-page.js.map +1 -1
- package/dist/cjs/nodes/navigation/navigation-navigate-to-url.d.ts.map +1 -1
- package/dist/cjs/nodes/navigation/navigation-navigate-to-url.js +5 -2
- package/dist/cjs/nodes/navigation/navigation-navigate-to-url.js.map +1 -1
- package/dist/cjs/nodes/payment/payment-charge-user.d.ts.map +1 -1
- package/dist/cjs/nodes/payment/payment-charge-user.js +7 -2
- package/dist/cjs/nodes/payment/payment-charge-user.js.map +1 -1
- package/dist/cjs/segment-splitter.d.ts +1 -1
- package/dist/cjs/segment-splitter.d.ts.map +1 -1
- package/dist/cjs/segment-splitter.js +29 -2
- package/dist/cjs/segment-splitter.js.map +1 -1
- package/dist/cjs/sent-email-log/index.d.ts +16 -0
- package/dist/cjs/sent-email-log/index.d.ts.map +1 -0
- package/dist/cjs/sent-email-log/index.js +53 -0
- package/dist/cjs/sent-email-log/index.js.map +1 -0
- package/dist/cjs/sent-email-log/sent-email-log-code.d.ts +4 -0
- package/dist/cjs/sent-email-log/sent-email-log-code.d.ts.map +1 -0
- package/dist/cjs/sent-email-log/sent-email-log-code.js +415 -0
- package/dist/cjs/sent-email-log/sent-email-log-code.js.map +1 -0
- package/dist/cjs/sent-email-log/sent-email-log-scope.d.ts +21 -0
- package/dist/cjs/sent-email-log/sent-email-log-scope.d.ts.map +1 -0
- package/dist/cjs/sent-email-log/sent-email-log-scope.js +36 -0
- package/dist/cjs/sent-email-log/sent-email-log-scope.js.map +1 -0
- package/dist/cjs/transactional-email-code.d.ts +39 -1
- package/dist/cjs/transactional-email-code.d.ts.map +1 -1
- package/dist/cjs/transactional-email-code.js +86 -12
- package/dist/cjs/transactional-email-code.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/webhook-generator.d.ts.map +1 -1
- package/dist/cjs/webhook-generator.js +9 -2
- package/dist/cjs/webhook-generator.js.map +1 -1
- package/dist/cjs/workflow-component-plugin.js +4 -3
- package/dist/cjs/workflow-component-plugin.js.map +1 -1
- package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-project-plugin.js +31 -3
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/cjs/workflow-utils-alias.d.ts +21 -0
- package/dist/cjs/workflow-utils-alias.d.ts.map +1 -0
- package/dist/cjs/workflow-utils-alias.js +27 -0
- package/dist/cjs/workflow-utils-alias.js.map +1 -0
- package/dist/esm/account-delete-route-generator.d.ts +20 -0
- package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
- package/dist/esm/account-delete-route-generator.js +30 -27
- package/dist/esm/account-delete-route-generator.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +57 -11
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/auth-generator.d.ts +2 -1
- package/dist/esm/auth-generator.d.ts.map +1 -1
- package/dist/esm/auth-generator.js +56 -19
- package/dist/esm/auth-generator.js.map +1 -1
- package/dist/esm/data-api-route-generator.d.ts.map +1 -1
- package/dist/esm/data-api-route-generator.js +19 -0
- package/dist/esm/data-api-route-generator.js.map +1 -1
- package/dist/esm/email-locale/email-locale-code.d.ts +25 -0
- package/dist/esm/email-locale/email-locale-code.d.ts.map +1 -0
- package/dist/esm/email-locale/email-locale-code.js +222 -0
- package/dist/esm/email-locale/email-locale-code.js.map +1 -0
- package/dist/esm/email-locale/email-locale-scope.d.ts +46 -0
- package/dist/esm/email-locale/email-locale-scope.d.ts.map +1 -0
- package/dist/esm/email-locale/email-locale-scope.js +47 -0
- package/dist/esm/email-locale/email-locale-scope.js.map +1 -0
- package/dist/esm/email-locale/index.d.ts +11 -0
- package/dist/esm/email-locale/index.d.ts.map +1 -0
- package/dist/esm/email-locale/index.js +28 -0
- package/dist/esm/email-locale/index.js.map +1 -0
- package/dist/esm/executor-generator.d.ts +11 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +49 -7
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/esm/invoice/api-routes-code.js +55 -6
- package/dist/esm/invoice/api-routes-code.js.map +1 -1
- package/dist/esm/invoice/email-sender-code.d.ts.map +1 -1
- package/dist/esm/invoice/email-sender-code.js +72 -31
- package/dist/esm/invoice/email-sender-code.js.map +1 -1
- package/dist/esm/invoice/index.d.ts +1 -4
- package/dist/esm/invoice/index.d.ts.map +1 -1
- package/dist/esm/invoice/index.js +7 -18
- package/dist/esm/invoice/index.js.map +1 -1
- package/dist/esm/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/esm/invoice/invoice-html-code.js +31 -8
- package/dist/esm/invoice/invoice-html-code.js.map +1 -1
- package/dist/esm/invoice/regional-invoice-tax-code.d.ts +25 -0
- package/dist/esm/invoice/regional-invoice-tax-code.d.ts.map +1 -0
- package/dist/esm/invoice/regional-invoice-tax-code.js +113 -0
- package/dist/esm/invoice/regional-invoice-tax-code.js.map +1 -0
- package/dist/esm/invoice/resolve-data-source.d.ts +6 -0
- package/dist/esm/invoice/resolve-data-source.d.ts.map +1 -0
- package/dist/esm/invoice/resolve-data-source.js +19 -0
- package/dist/esm/invoice/resolve-data-source.js.map +1 -0
- package/dist/esm/nodes/account/account-delete-current.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-delete-current.js +10 -3
- package/dist/esm/nodes/account/account-delete-current.js.map +1 -1
- package/dist/esm/nodes/account/account-login.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-login.js +3 -1
- package/dist/esm/nodes/account/account-login.js.map +1 -1
- package/dist/esm/nodes/account/account-logout.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-logout.js +4 -2
- package/dist/esm/nodes/account/account-logout.js.map +1 -1
- package/dist/esm/nodes/account/account-signup.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-signup.js +12 -2
- package/dist/esm/nodes/account/account-signup.js.map +1 -1
- package/dist/esm/nodes/account/account-social-login.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-social-login.js +4 -2
- package/dist/esm/nodes/account/account-social-login.js.map +1 -1
- package/dist/esm/nodes/cart/cart-clear.d.ts.map +1 -1
- package/dist/esm/nodes/cart/cart-clear.js +6 -0
- package/dist/esm/nodes/cart/cart-clear.js.map +1 -1
- package/dist/esm/nodes/cart/cart-get-total.d.ts.map +1 -1
- package/dist/esm/nodes/cart/cart-get-total.js +81 -5
- package/dist/esm/nodes/cart/cart-get-total.js.map +1 -1
- package/dist/esm/nodes/data/data-create-item.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-create-item.js +16 -0
- package/dist/esm/nodes/data/data-create-item.js.map +1 -1
- package/dist/esm/nodes/ecommerce/ecommerce-generate-invoice.d.ts.map +1 -1
- package/dist/esm/nodes/ecommerce/ecommerce-generate-invoice.js +6 -0
- package/dist/esm/nodes/ecommerce/ecommerce-generate-invoice.js.map +1 -1
- package/dist/esm/nodes/navigation/navigation-go-to-page.d.ts.map +1 -1
- package/dist/esm/nodes/navigation/navigation-go-to-page.js +4 -1
- package/dist/esm/nodes/navigation/navigation-go-to-page.js.map +1 -1
- package/dist/esm/nodes/navigation/navigation-navigate-to-url.d.ts.map +1 -1
- package/dist/esm/nodes/navigation/navigation-navigate-to-url.js +5 -2
- package/dist/esm/nodes/navigation/navigation-navigate-to-url.js.map +1 -1
- package/dist/esm/nodes/payment/payment-charge-user.d.ts.map +1 -1
- package/dist/esm/nodes/payment/payment-charge-user.js +7 -2
- package/dist/esm/nodes/payment/payment-charge-user.js.map +1 -1
- package/dist/esm/segment-splitter.d.ts +1 -1
- package/dist/esm/segment-splitter.d.ts.map +1 -1
- package/dist/esm/segment-splitter.js +29 -2
- package/dist/esm/segment-splitter.js.map +1 -1
- package/dist/esm/sent-email-log/index.d.ts +16 -0
- package/dist/esm/sent-email-log/index.d.ts.map +1 -0
- package/dist/esm/sent-email-log/index.js +45 -0
- package/dist/esm/sent-email-log/index.js.map +1 -0
- package/dist/esm/sent-email-log/sent-email-log-code.d.ts +4 -0
- package/dist/esm/sent-email-log/sent-email-log-code.d.ts.map +1 -0
- package/dist/esm/sent-email-log/sent-email-log-code.js +411 -0
- package/dist/esm/sent-email-log/sent-email-log-code.js.map +1 -0
- package/dist/esm/sent-email-log/sent-email-log-scope.d.ts +21 -0
- package/dist/esm/sent-email-log/sent-email-log-scope.d.ts.map +1 -0
- package/dist/esm/sent-email-log/sent-email-log-scope.js +31 -0
- package/dist/esm/sent-email-log/sent-email-log-scope.js.map +1 -0
- package/dist/esm/transactional-email-code.d.ts +39 -1
- package/dist/esm/transactional-email-code.d.ts.map +1 -1
- package/dist/esm/transactional-email-code.js +81 -10
- package/dist/esm/transactional-email-code.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/webhook-generator.d.ts.map +1 -1
- package/dist/esm/webhook-generator.js +9 -2
- package/dist/esm/webhook-generator.js.map +1 -1
- package/dist/esm/workflow-component-plugin.js +4 -3
- package/dist/esm/workflow-component-plugin.js.map +1 -1
- package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-project-plugin.js +32 -4
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/dist/esm/workflow-utils-alias.d.ts +21 -0
- package/dist/esm/workflow-utils-alias.d.ts.map +1 -0
- package/dist/esm/workflow-utils-alias.js +21 -0
- package/dist/esm/workflow-utils-alias.js.map +1 -0
- package/package.json +4 -4
- package/src/account-delete-route-generator.ts +36 -7
- package/src/api-route-generator.ts +64 -11
- package/src/auth-generator.ts +63 -18
- package/src/data-api-route-generator.ts +19 -0
- package/src/email-locale/email-locale-code.ts +239 -0
- package/src/email-locale/email-locale-scope.ts +65 -0
- package/src/email-locale/index.ts +46 -0
- package/src/executor-generator.ts +65 -7
- package/src/index.ts +8 -0
- package/src/invoice/api-routes-code.ts +65 -6
- package/src/invoice/email-sender-code.ts +72 -31
- package/src/invoice/index.ts +7 -23
- package/src/invoice/invoice-html-code.ts +31 -8
- package/src/invoice/regional-invoice-tax-code.ts +112 -0
- package/src/invoice/resolve-data-source.ts +25 -0
- package/src/nodes/account/account-delete-current.ts +10 -3
- package/src/nodes/account/account-login.ts +3 -1
- package/src/nodes/account/account-logout.ts +4 -2
- package/src/nodes/account/account-signup.ts +12 -2
- package/src/nodes/account/account-social-login.ts +7 -2
- package/src/nodes/cart/cart-clear.ts +6 -0
- package/src/nodes/cart/cart-get-total.ts +94 -5
- package/src/nodes/data/data-create-item.ts +16 -0
- package/src/nodes/ecommerce/ecommerce-generate-invoice.ts +6 -0
- package/src/nodes/navigation/navigation-go-to-page.ts +5 -1
- package/src/nodes/navigation/navigation-navigate-to-url.ts +6 -2
- package/src/nodes/payment/payment-charge-user.ts +13 -2
- package/src/nodes/workflow-utils-global.d.ts +12 -0
- package/src/segment-splitter.ts +33 -2
- package/src/sent-email-log/index.ts +57 -0
- package/src/sent-email-log/sent-email-log-code.ts +419 -0
- package/src/sent-email-log/sent-email-log-scope.ts +36 -0
- package/src/transactional-email-code.ts +112 -4
- package/src/webhook-generator.ts +11 -2
- package/src/workflow-component-plugin.ts +4 -3
- package/src/workflow-project-plugin.ts +36 -4
- package/src/workflow-utils-alias.ts +26 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateEmailLocaleModuleCode,
|
|
3
|
+
resolveEmailLocaleConfig,
|
|
4
|
+
EMAIL_LOCALE_HEADER,
|
|
5
|
+
} from '../src/email-locale'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The shared locale module decides which language a customer email goes out
|
|
9
|
+
* in and which path a navigation lands on. It is EXECUTED here rather than
|
|
10
|
+
* grepped: every helper must be total (an unknown locale can never select a
|
|
11
|
+
* template that does not exist, nor send a visitor to a locale the app does
|
|
12
|
+
* not serve), and the precedence between the explicit header, the referring
|
|
13
|
+
* page and the Next.js cookie is what keeps a request without the header from
|
|
14
|
+
* silently falling back to the main language.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface EmailLocaleModule {
|
|
18
|
+
EMAIL_LOCALES: string[]
|
|
19
|
+
DEFAULT_EMAIL_LOCALE: string
|
|
20
|
+
normalizeEmailLocale: (value: unknown) => string | null
|
|
21
|
+
resolveRequestLocale: (req: unknown) => string
|
|
22
|
+
getClientLocale: () => string | null
|
|
23
|
+
localizeHref: (href: unknown, locale: unknown) => unknown
|
|
24
|
+
pickLocalizedTemplate: (
|
|
25
|
+
base: { subject?: string; body?: string },
|
|
26
|
+
localized: unknown,
|
|
27
|
+
locale: unknown
|
|
28
|
+
) => { subject?: string; body?: string; locale: string }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const loadModule = (config: { locales: string[]; defaultLocale: string }): EmailLocaleModule => {
|
|
32
|
+
const code = generateEmailLocaleModuleCode(config)
|
|
33
|
+
const moduleShim = { exports: {} as EmailLocaleModule }
|
|
34
|
+
// eslint-disable-next-line no-new-func
|
|
35
|
+
new Function('module', 'exports', 'require', code)(moduleShim, moduleShim.exports, () => ({}))
|
|
36
|
+
return moduleShim.exports
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const request = (headers: Record<string, string | string[]>) => ({ headers })
|
|
40
|
+
|
|
41
|
+
describe('resolveEmailLocaleConfig', () => {
|
|
42
|
+
it('reads the project languages and the main locale off the UIDL', () => {
|
|
43
|
+
const uidl = {
|
|
44
|
+
internationalization: {
|
|
45
|
+
main: { name: 'English', locale: 'en' },
|
|
46
|
+
languages: { en: 'English', es: 'Spanish' },
|
|
47
|
+
translations: {},
|
|
48
|
+
},
|
|
49
|
+
} as any
|
|
50
|
+
expect(resolveEmailLocaleConfig(uidl)).toEqual({ locales: ['en', 'es'], defaultLocale: 'en' })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('always lists the main locale, even when the language map omits it', () => {
|
|
54
|
+
const uidl = {
|
|
55
|
+
internationalization: {
|
|
56
|
+
main: { name: 'French', locale: 'fr' },
|
|
57
|
+
languages: { es: 'Spanish' },
|
|
58
|
+
translations: {},
|
|
59
|
+
},
|
|
60
|
+
} as any
|
|
61
|
+
expect(resolveEmailLocaleConfig(uidl)).toEqual({ locales: ['fr', 'es'], defaultLocale: 'fr' })
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('has no locales at all for a project without internationalization', () => {
|
|
65
|
+
expect(resolveEmailLocaleConfig({} as any)).toEqual({ locales: [], defaultLocale: '' })
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('email-locale module', () => {
|
|
70
|
+
const locales = loadModule({ locales: ['en', 'es', 'pt-BR'], defaultLocale: 'en' })
|
|
71
|
+
|
|
72
|
+
describe('normalizeEmailLocale', () => {
|
|
73
|
+
it('answers the canonical code, case-insensitively', () => {
|
|
74
|
+
expect(locales.normalizeEmailLocale('es')).toBe('es')
|
|
75
|
+
expect(locales.normalizeEmailLocale(' ES ')).toBe('es')
|
|
76
|
+
expect(locales.normalizeEmailLocale('pt-br')).toBe('pt-BR')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('rejects anything that is not a project locale', () => {
|
|
80
|
+
expect(locales.normalizeEmailLocale('de')).toBeNull()
|
|
81
|
+
expect(locales.normalizeEmailLocale('')).toBeNull()
|
|
82
|
+
expect(locales.normalizeEmailLocale(null)).toBeNull()
|
|
83
|
+
expect(locales.normalizeEmailLocale(42)).toBeNull()
|
|
84
|
+
expect(locales.normalizeEmailLocale({ locale: 'es' })).toBeNull()
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('resolveRequestLocale', () => {
|
|
89
|
+
it('prefers the explicit header the client runtime sends', () => {
|
|
90
|
+
expect(
|
|
91
|
+
locales.resolveRequestLocale(
|
|
92
|
+
request({
|
|
93
|
+
[EMAIL_LOCALE_HEADER]: 'es',
|
|
94
|
+
cookie: 'NEXT_LOCALE=pt-BR',
|
|
95
|
+
referer: 'https://shop.example/pt-BR/checkout',
|
|
96
|
+
})
|
|
97
|
+
)
|
|
98
|
+
).toBe('es')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('takes the first value of a repeated header', () => {
|
|
102
|
+
expect(locales.resolveRequestLocale(request({ [EMAIL_LOCALE_HEADER]: ['es', 'en'] }))).toBe(
|
|
103
|
+
'es'
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('falls back to the locale prefix of the referring page', () => {
|
|
108
|
+
expect(
|
|
109
|
+
locales.resolveRequestLocale(request({ referer: 'https://shop.example/es/cart' }))
|
|
110
|
+
).toBe('es')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('trusts the page the request came from over a stored locale cookie', () => {
|
|
114
|
+
expect(
|
|
115
|
+
locales.resolveRequestLocale(
|
|
116
|
+
request({ cookie: 'session=abc; NEXT_LOCALE=pt-BR; other=1', referer: 'https://x/es/' })
|
|
117
|
+
)
|
|
118
|
+
).toBe('es')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('falls back to the Next.js locale cookie when nothing names the page', () => {
|
|
122
|
+
expect(
|
|
123
|
+
locales.resolveRequestLocale(request({ cookie: 'session=abc; NEXT_LOCALE=pt-BR; other=1' }))
|
|
124
|
+
).toBe('pt-BR')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('reads an unprefixed referring page as the default language', () => {
|
|
128
|
+
expect(locales.resolveRequestLocale(request({ referer: 'https://shop.example/cart' }))).toBe(
|
|
129
|
+
'en'
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('ignores values that are not project locales and lands on the default', () => {
|
|
134
|
+
expect(
|
|
135
|
+
locales.resolveRequestLocale(
|
|
136
|
+
request({
|
|
137
|
+
[EMAIL_LOCALE_HEADER]: 'de',
|
|
138
|
+
cookie: 'NEXT_LOCALE=fr',
|
|
139
|
+
referer: 'not a url',
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
).toBe('en')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('answers the default language for a request without headers', () => {
|
|
146
|
+
expect(locales.resolveRequestLocale(null)).toBe('en')
|
|
147
|
+
expect(locales.resolveRequestLocale({})).toBe('en')
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('pickLocalizedTemplate', () => {
|
|
152
|
+
const base = { subject: 'Hello {{name}}', body: '<p>Hi {{name}}</p>' }
|
|
153
|
+
const localized = {
|
|
154
|
+
es: { subject: 'Hola {{name}}', body: '<p>Hola {{name}}</p>' },
|
|
155
|
+
'pt-BR': { body: '<p>Olá {{name}}</p>' },
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
it('returns the copy of the requested language', () => {
|
|
159
|
+
expect(locales.pickLocalizedTemplate(base, localized, 'es')).toEqual({
|
|
160
|
+
subject: 'Hola {{name}}',
|
|
161
|
+
body: '<p>Hola {{name}}</p>',
|
|
162
|
+
locale: 'es',
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('keeps the base subject when the language has no subject of its own', () => {
|
|
167
|
+
expect(locales.pickLocalizedTemplate(base, localized, 'pt-br')).toEqual({
|
|
168
|
+
subject: 'Hello {{name}}',
|
|
169
|
+
body: '<p>Olá {{name}}</p>',
|
|
170
|
+
locale: 'pt-BR',
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('returns the base copy for the default language', () => {
|
|
175
|
+
expect(locales.pickLocalizedTemplate(base, localized, 'en')).toEqual({
|
|
176
|
+
...base,
|
|
177
|
+
locale: 'en',
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('returns the base copy for a language without a copy, an unknown one, or none', () => {
|
|
182
|
+
const expected = { ...base, locale: 'en' }
|
|
183
|
+
expect(locales.pickLocalizedTemplate(base, { es: localized.es }, 'pt-BR')).toEqual(expected)
|
|
184
|
+
expect(locales.pickLocalizedTemplate(base, localized, 'de')).toEqual(expected)
|
|
185
|
+
expect(locales.pickLocalizedTemplate(base, localized, undefined)).toEqual(expected)
|
|
186
|
+
expect(locales.pickLocalizedTemplate(base, null, 'es')).toEqual(expected)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('never selects a copy with an empty body', () => {
|
|
190
|
+
expect(
|
|
191
|
+
locales.pickLocalizedTemplate(base, { es: { subject: 'Hola', body: '' } }, 'es')
|
|
192
|
+
).toEqual({ ...base, locale: 'en' })
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('getClientLocale', () => {
|
|
197
|
+
const globalRef = global as unknown as Record<string, unknown>
|
|
198
|
+
|
|
199
|
+
const withBrowser = <T>(browser: Record<string, unknown>, run: () => T): T => {
|
|
200
|
+
globalRef.window = browser.window
|
|
201
|
+
globalRef.document = browser.document
|
|
202
|
+
try {
|
|
203
|
+
return run()
|
|
204
|
+
} finally {
|
|
205
|
+
delete globalRef.window
|
|
206
|
+
delete globalRef.document
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
it('is inert outside a browser', () => {
|
|
211
|
+
expect(locales.getClientLocale()).toBeNull()
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('reads the router live, so a client-side language switch is not lost', () => {
|
|
215
|
+
// After `router.push(..., { locale: 'es' })` Next.js updates the router
|
|
216
|
+
// and <html lang>, but the page data keeps the locale the document was
|
|
217
|
+
// served in.
|
|
218
|
+
expect(
|
|
219
|
+
withBrowser(
|
|
220
|
+
{
|
|
221
|
+
window: { next: { router: { locale: 'es' } }, __NEXT_DATA__: { locale: 'en' } },
|
|
222
|
+
document: { documentElement: { lang: 'es' } },
|
|
223
|
+
},
|
|
224
|
+
() => locales.getClientLocale()
|
|
225
|
+
)
|
|
226
|
+
).toBe('es')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('falls back to <html lang>, then to the page data', () => {
|
|
230
|
+
expect(
|
|
231
|
+
withBrowser(
|
|
232
|
+
{
|
|
233
|
+
window: { __NEXT_DATA__: { locale: 'en' } },
|
|
234
|
+
document: { documentElement: { lang: 'es' } },
|
|
235
|
+
},
|
|
236
|
+
() => locales.getClientLocale()
|
|
237
|
+
)
|
|
238
|
+
).toBe('es')
|
|
239
|
+
expect(
|
|
240
|
+
withBrowser({ window: { __NEXT_DATA__: { locale: 'pt-br' } }, document: {} }, () =>
|
|
241
|
+
locales.getClientLocale()
|
|
242
|
+
)
|
|
243
|
+
).toBe('pt-BR')
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('ignores a candidate that is not a project locale', () => {
|
|
247
|
+
expect(
|
|
248
|
+
withBrowser(
|
|
249
|
+
{
|
|
250
|
+
window: { next: { router: { locale: 'de' } } },
|
|
251
|
+
document: { documentElement: { lang: 'x' } },
|
|
252
|
+
},
|
|
253
|
+
() => locales.getClientLocale()
|
|
254
|
+
)
|
|
255
|
+
).toBeNull()
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('localizeHref', () => {
|
|
260
|
+
it('prefixes a site-relative page path with a non-default locale', () => {
|
|
261
|
+
expect(locales.localizeHref('/', 'es')).toBe('/es')
|
|
262
|
+
expect(locales.localizeHref('/sign-in', 'es')).toBe('/es/sign-in')
|
|
263
|
+
expect(locales.localizeHref('/orders/ORD-42?payment=success', 'pt-br')).toBe(
|
|
264
|
+
'/pt-BR/orders/ORD-42?payment=success'
|
|
265
|
+
)
|
|
266
|
+
expect(locales.localizeHref('/?ref=1#top', 'es')).toBe('/es?ref=1#top')
|
|
267
|
+
expect(locales.localizeHref('/products/', 'es')).toBe('/es/products/')
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('keeps the bare path for the default language, an unknown locale and no locale', () => {
|
|
271
|
+
expect(locales.localizeHref('/sign-in', 'en')).toBe('/sign-in')
|
|
272
|
+
expect(locales.localizeHref('/sign-in', 'de')).toBe('/sign-in')
|
|
273
|
+
expect(locales.localizeHref('/sign-in', undefined)).toBe('/sign-in')
|
|
274
|
+
expect(locales.localizeHref('/sign-in', null)).toBe('/sign-in')
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('never touches a path that already names a locale', () => {
|
|
278
|
+
expect(locales.localizeHref('/es/cart', 'es')).toBe('/es/cart')
|
|
279
|
+
expect(locales.localizeHref('/es', 'es')).toBe('/es')
|
|
280
|
+
expect(locales.localizeHref('/en/cart', 'es')).toBe('/en/cart')
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('never touches an API route, a Next.js internal path or a file', () => {
|
|
284
|
+
expect(locales.localizeHref('/api/invoices/download?id=1', 'es')).toBe(
|
|
285
|
+
'/api/invoices/download?id=1'
|
|
286
|
+
)
|
|
287
|
+
expect(locales.localizeHref('/api', 'es')).toBe('/api')
|
|
288
|
+
expect(locales.localizeHref('/_next/static/x.js', 'es')).toBe('/_next/static/x.js')
|
|
289
|
+
expect(locales.localizeHref('/brochure.pdf', 'es')).toBe('/brochure.pdf')
|
|
290
|
+
expect(locales.localizeHref('/sitemap.xml?v=2', 'es')).toBe('/sitemap.xml?v=2')
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('never touches anything that is not a site-relative path', () => {
|
|
294
|
+
expect(locales.localizeHref('https://shop.example/cart', 'es')).toBe(
|
|
295
|
+
'https://shop.example/cart'
|
|
296
|
+
)
|
|
297
|
+
expect(locales.localizeHref('//cdn.example/x', 'es')).toBe('//cdn.example/x')
|
|
298
|
+
expect(locales.localizeHref('#top', 'es')).toBe('#top')
|
|
299
|
+
expect(locales.localizeHref('mailto:a@b.c', 'es')).toBe('mailto:a@b.c')
|
|
300
|
+
expect(locales.localizeHref('products', 'es')).toBe('products')
|
|
301
|
+
expect(locales.localizeHref('', 'es')).toBe('')
|
|
302
|
+
expect(locales.localizeHref(undefined, 'es')).toBeUndefined()
|
|
303
|
+
expect(locales.localizeHref(42, 'es')).toBe(42)
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
describe('email-locale module without internationalization', () => {
|
|
309
|
+
const none = loadModule({ locales: [], defaultLocale: '' })
|
|
310
|
+
|
|
311
|
+
it('recognises no locale and keeps the single template', () => {
|
|
312
|
+
expect(none.normalizeEmailLocale('en')).toBeNull()
|
|
313
|
+
expect(none.resolveRequestLocale(request({ [EMAIL_LOCALE_HEADER]: 'en' }))).toBe('')
|
|
314
|
+
expect(
|
|
315
|
+
none.pickLocalizedTemplate({ subject: 'S', body: 'B' }, { en: { body: 'X' } }, 'en')
|
|
316
|
+
).toEqual({ subject: 'S', body: 'B', locale: '' })
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('never prefixes a path', () => {
|
|
320
|
+
expect(none.localizeHref('/sign-in', 'en')).toBe('/sign-in')
|
|
321
|
+
expect(none.localizeHref('/', 'es')).toBe('/')
|
|
322
|
+
})
|
|
323
|
+
})
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { UIDLInvoiceEmailDelivery, UIDLInvoiceSettings } from '@teleporthq/teleport-types'
|
|
2
|
+
import { generateSignupRouteFile } from '../src/auth-generator'
|
|
3
|
+
import { generateAccountDeleteRoute } from '../src/account-delete-route-generator'
|
|
4
|
+
import { generateEmailSenderCode } from '../src/invoice/email-sender-code'
|
|
5
|
+
import { generateInvoiceGenerateRouteCode } from '../src/invoice/api-routes-code'
|
|
6
|
+
import { generateDataAPIRoute } from '../src/data-api-route-generator'
|
|
7
|
+
import { generateEmailLocaleModuleCode } from '../src/email-locale'
|
|
8
|
+
import { readLocalizedEmailTemplates } from '../src/transactional-email-code'
|
|
9
|
+
import { nodeRegistry } from '../src'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The email senders that live OUTSIDE the workflow runtime — the welcome and
|
|
13
|
+
* farewell routes, the invoice sender, the data-api that stamps an order's
|
|
14
|
+
* language — each read the shared locale module. These tests pin that every
|
|
15
|
+
* one of them selects the copy of the language it resolved, and that the
|
|
16
|
+
* order row keeps the storefront language it was written from.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const LOCALIZED = {
|
|
20
|
+
es: { subject: 'Bienvenido {{userName}}', body: '<p>Hola {{userName}}</p>' },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const extractFunctionSource = (haystack: string, funcDecl: string): string => {
|
|
24
|
+
const startIdx = haystack.indexOf(funcDecl)
|
|
25
|
+
if (startIdx === -1) {
|
|
26
|
+
throw new Error('Helper not found: ' + funcDecl)
|
|
27
|
+
}
|
|
28
|
+
let depth = 0
|
|
29
|
+
let i = haystack.indexOf('{', startIdx)
|
|
30
|
+
for (; i < haystack.length; i++) {
|
|
31
|
+
const ch = haystack.charAt(i)
|
|
32
|
+
if (ch === '{') {
|
|
33
|
+
depth++
|
|
34
|
+
} else if (ch === '}') {
|
|
35
|
+
depth--
|
|
36
|
+
if (depth === 0) {
|
|
37
|
+
return haystack.slice(startIdx, i + 1)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Unbalanced braces for ' + funcDecl)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const loadLocaleModule = () => {
|
|
45
|
+
const moduleShim = { exports: {} as Record<string, unknown> }
|
|
46
|
+
// eslint-disable-next-line no-new-func
|
|
47
|
+
new Function(
|
|
48
|
+
'module',
|
|
49
|
+
'exports',
|
|
50
|
+
generateEmailLocaleModuleCode({ locales: ['en', 'es'], defaultLocale: 'en' })
|
|
51
|
+
)(moduleShim, moduleShim.exports)
|
|
52
|
+
return moduleShim.exports
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe('readLocalizedEmailTemplates', () => {
|
|
56
|
+
it('keeps only well-formed copies', () => {
|
|
57
|
+
expect(
|
|
58
|
+
readLocalizedEmailTemplates({
|
|
59
|
+
es: { subject: 'S', body: 'B' },
|
|
60
|
+
fr: { body: 'B' },
|
|
61
|
+
de: { subject: 'only a subject' },
|
|
62
|
+
it: { subject: '', body: '' },
|
|
63
|
+
pt: 'B',
|
|
64
|
+
})
|
|
65
|
+
).toEqual({ es: { subject: 'S', body: 'B' }, fr: { body: 'B' } })
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('answers no copies for anything that is not a map', () => {
|
|
69
|
+
expect(readLocalizedEmailTemplates(undefined)).toEqual({})
|
|
70
|
+
expect(readLocalizedEmailTemplates([{ body: 'B' }])).toEqual({})
|
|
71
|
+
expect(readLocalizedEmailTemplates('es')).toEqual({})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('welcome and farewell routes', () => {
|
|
76
|
+
const auth = { enabled: true, dataSourceType: 'postgresql' } as any
|
|
77
|
+
const signup = generateSignupRouteFile(auth, {
|
|
78
|
+
emailProvider: 'resend',
|
|
79
|
+
emailSubject: 'Welcome {{userName}}',
|
|
80
|
+
emailBodyHtml: '<p>Hi {{userName}}</p>',
|
|
81
|
+
localizedTemplates: LOCALIZED,
|
|
82
|
+
})
|
|
83
|
+
const del = generateAccountDeleteRoute({
|
|
84
|
+
emailProvider: 'resend',
|
|
85
|
+
emailSubject: 'Goodbye {{userName}}',
|
|
86
|
+
emailBodyHtml: '<p>Bye {{userName}}</p>',
|
|
87
|
+
localizedTemplates: LOCALIZED,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('bake the per-language copies and require the locale module', () => {
|
|
91
|
+
expect(signup).toContain(`const WELCOME_EMAIL_LOCALIZED = ${JSON.stringify(LOCALIZED)};`)
|
|
92
|
+
expect(signup).toContain("var __emailLocale = require('../../../utils/email/email-locale');")
|
|
93
|
+
expect(del).toContain(`const EMAIL_LOCALIZED = ${JSON.stringify(LOCALIZED)};`)
|
|
94
|
+
expect(del).toContain("var __emailLocale = require('../../../utils/email/email-locale');")
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('send the copy of the request language', () => {
|
|
98
|
+
expect(signup).toContain('var copy = resolveWelcomeEmailCopy(locale);')
|
|
99
|
+
expect(signup).toContain('var subject = fillTemplate(copy.subject, tokenValues);')
|
|
100
|
+
expect(signup).toContain('var html = fillTemplate(copy.body, tokenValues);')
|
|
101
|
+
expect(del).toContain('var copy = resolveFarewellEmailCopy(locale);')
|
|
102
|
+
expect(del).toContain('var subject = fillTemplate(copy.subject, tokenValues);')
|
|
103
|
+
expect(del).toContain('var html = fillTemplate(copy.body, tokenValues);')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('resolve the language off the request at the call site', () => {
|
|
107
|
+
expect(signup).toContain('newUser && newUser.id, __emailLocale.resolveRequestLocale(req));')
|
|
108
|
+
expect(del).toContain('}, userId, __emailLocale.resolveRequestLocale(req));')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('the copy selector honours the locale module rules', () => {
|
|
112
|
+
const locale = loadLocaleModule()
|
|
113
|
+
const selector = extractFunctionSource(signup, 'function resolveWelcomeEmailCopy(')
|
|
114
|
+
// eslint-disable-next-line no-new-func
|
|
115
|
+
const resolveWelcomeEmailCopy = new Function(
|
|
116
|
+
'__emailLocale',
|
|
117
|
+
'WELCOME_EMAIL_SUBJECT',
|
|
118
|
+
'WELCOME_EMAIL_BODY_HTML',
|
|
119
|
+
'WELCOME_EMAIL_LOCALIZED',
|
|
120
|
+
`${selector}; return resolveWelcomeEmailCopy;`
|
|
121
|
+
)(locale, 'Welcome {{userName}}', '<p>Hi {{userName}}</p>', LOCALIZED)
|
|
122
|
+
expect(resolveWelcomeEmailCopy('es')).toEqual({
|
|
123
|
+
subject: 'Bienvenido {{userName}}',
|
|
124
|
+
body: '<p>Hola {{userName}}</p>',
|
|
125
|
+
locale: 'es',
|
|
126
|
+
})
|
|
127
|
+
expect(resolveWelcomeEmailCopy('en')).toEqual({
|
|
128
|
+
subject: 'Welcome {{userName}}',
|
|
129
|
+
body: '<p>Hi {{userName}}</p>',
|
|
130
|
+
locale: 'en',
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('the signup route never inserts the copies as a user column', () => {
|
|
135
|
+
expect(signup).toContain('"localizedTemplates":1')
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe('invoice email', () => {
|
|
140
|
+
const delivery: UIDLInvoiceEmailDelivery = {
|
|
141
|
+
enabled: true,
|
|
142
|
+
provider: 'resend',
|
|
143
|
+
fromEmail: 'billing@example.com',
|
|
144
|
+
fromName: 'Billing',
|
|
145
|
+
subject: 'Invoice {{invoiceNumber}}',
|
|
146
|
+
body: '<p>Invoice {{invoiceNumber}}</p>',
|
|
147
|
+
secretKeys: {},
|
|
148
|
+
localizedTemplates: {
|
|
149
|
+
es: { subject: 'Factura {{invoiceNumber}}', body: '<p>Factura {{invoiceNumber}}</p>' },
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
it('the sender picks the copy of the invoice locale', () => {
|
|
154
|
+
const sender = generateEmailSenderCode(delivery)
|
|
155
|
+
expect(sender).toContain("var emailLocale = require('../email/email-locale');")
|
|
156
|
+
expect(sender).toContain(
|
|
157
|
+
`var LOCALIZED_TEMPLATES = ${JSON.stringify(delivery.localizedTemplates)};`
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const locale = loadLocaleModule()
|
|
161
|
+
const buildEmailData = extractFunctionSource(sender, 'function buildEmailData(')
|
|
162
|
+
const pdfGenerator = {
|
|
163
|
+
buildDataContext: (data: { invoiceNumber: string }) => ({
|
|
164
|
+
invoiceNumber: data.invoiceNumber,
|
|
165
|
+
}),
|
|
166
|
+
}
|
|
167
|
+
const replacePlaceholders = (template: string, data: Record<string, string>) =>
|
|
168
|
+
template.replace(/\{\{(\w+)\}\}/g, (_m, key) => data[key] || '')
|
|
169
|
+
// eslint-disable-next-line no-new-func
|
|
170
|
+
const build = new Function(
|
|
171
|
+
'emailLocale',
|
|
172
|
+
'pdfGenerator',
|
|
173
|
+
'replacePlaceholders',
|
|
174
|
+
'SUBJECT_TEMPLATE',
|
|
175
|
+
'BODY_TEMPLATE',
|
|
176
|
+
'LOCALIZED_TEMPLATES',
|
|
177
|
+
`${buildEmailData}; return buildEmailData;`
|
|
178
|
+
)(
|
|
179
|
+
locale,
|
|
180
|
+
pdfGenerator,
|
|
181
|
+
replacePlaceholders,
|
|
182
|
+
delivery.subject,
|
|
183
|
+
delivery.body,
|
|
184
|
+
delivery.localizedTemplates
|
|
185
|
+
)
|
|
186
|
+
expect(build({ invoiceNumber: 'INV-7', locale: 'es', customerEmail: 'a@b.c' })).toMatchObject({
|
|
187
|
+
subject: 'Factura INV-7',
|
|
188
|
+
body: '<p>Factura INV-7</p>',
|
|
189
|
+
})
|
|
190
|
+
expect(build({ invoiceNumber: 'INV-7', customerEmail: 'a@b.c' })).toMatchObject({
|
|
191
|
+
subject: 'Invoice INV-7',
|
|
192
|
+
body: '<p>Invoice INV-7</p>',
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('the generate route resolves the locale: order row, then caller, then request', () => {
|
|
197
|
+
const settings = {
|
|
198
|
+
invoicePrefix: 'INV-',
|
|
199
|
+
defaultTaxRate: 0,
|
|
200
|
+
showDiscount: false,
|
|
201
|
+
template: { document: null as unknown },
|
|
202
|
+
tables: { invoicesTable: 'teleport_invoices', invoiceItemsTable: 'teleport_invoice_items' },
|
|
203
|
+
emailDelivery: delivery,
|
|
204
|
+
} as unknown as UIDLInvoiceSettings
|
|
205
|
+
const route = generateInvoiceGenerateRouteCode(settings)
|
|
206
|
+
expect(route).toContain("var emailLocale = require('../../../utils/email/email-locale');")
|
|
207
|
+
expect(route).toContain('var invoiceLocale = emailLocale.normalizeEmailLocale(orderRow.locale)')
|
|
208
|
+
expect(route).toContain('|| emailLocale.normalizeEmailLocale(body.locale)')
|
|
209
|
+
expect(route).toContain('|| emailLocale.resolveRequestLocale(req);')
|
|
210
|
+
expect(route).toContain('locale: invoiceLocale,')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('a project without email delivery needs no locale module', () => {
|
|
214
|
+
const route = generateInvoiceGenerateRouteCode({
|
|
215
|
+
invoicePrefix: 'INV-',
|
|
216
|
+
emailDelivery: { enabled: false },
|
|
217
|
+
} as unknown as UIDLInvoiceSettings)
|
|
218
|
+
expect(route).not.toContain('email-locale')
|
|
219
|
+
expect(route).not.toContain('invoiceLocale')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('the generate-invoice node forwards the run locale as a hint', () => {
|
|
223
|
+
const source = nodeRegistry['ecommerce-generate-invoice'].generateHandler()
|
|
224
|
+
expect(source).toContain('payload.locale = runLocale')
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('data-api stamps the storefront language on an order', () => {
|
|
229
|
+
const code = generateDataAPIRoute()
|
|
230
|
+
const stamp = extractFunctionSource(code, 'function stampRequestLocale(')
|
|
231
|
+
// eslint-disable-next-line no-new-func
|
|
232
|
+
const stampRequestLocale = new Function(
|
|
233
|
+
'LOCALE_STAMPED_TABLES',
|
|
234
|
+
'LOCALE_COLUMN',
|
|
235
|
+
`${stamp}; return stampRequestLocale;`
|
|
236
|
+
)({ teleport_orders: 1 }, 'locale')
|
|
237
|
+
|
|
238
|
+
it('adds the locale to an orders insert when the table has the column', () => {
|
|
239
|
+
const entries = [['status', 'pending']]
|
|
240
|
+
expect(
|
|
241
|
+
stampRequestLocale('teleport_orders', entries, { locale: 'character varying' }, 'es')
|
|
242
|
+
).toEqual([
|
|
243
|
+
['status', 'pending'],
|
|
244
|
+
['locale', 'es'],
|
|
245
|
+
])
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('leaves the insert alone when the column, the locale or the table does not apply', () => {
|
|
249
|
+
const entries = [['status', 'pending']]
|
|
250
|
+
expect(stampRequestLocale('teleport_orders', entries, {}, 'es')).toBe(entries)
|
|
251
|
+
expect(stampRequestLocale('teleport_orders', entries, { locale: 'text' }, '')).toBe(entries)
|
|
252
|
+
expect(stampRequestLocale('teleport_products', entries, { locale: 'text' }, 'es')).toBe(entries)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('never overrides a locale the workflow mapped itself', () => {
|
|
256
|
+
const entries = [['locale', 'fr']]
|
|
257
|
+
expect(stampRequestLocale('teleport_orders', entries, { locale: 'text' }, 'es')).toBe(entries)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('is wired into handleCreate from the request body', () => {
|
|
261
|
+
expect(code).toContain(
|
|
262
|
+
'entries = stampRequestLocale(tableName, entries, colTypes, body.__requestLocale);'
|
|
263
|
+
)
|
|
264
|
+
expect(nodeRegistry['data-create-item'].generateHandler()).toContain(
|
|
265
|
+
'reqBody.__requestLocale = __runLocale'
|
|
266
|
+
)
|
|
267
|
+
})
|
|
268
|
+
})
|