@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
|
@@ -140,6 +140,66 @@ describe('invoice PDF renderer — delivery fee', () => {
|
|
|
140
140
|
expect(withoutFee).not.toContain('<span>Delivery</span>')
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
+
it('renders the template Delivery row gated on Invoice.hasShipping only when there is a fee', () => {
|
|
144
|
+
// The GUI emits the row's rendering condition as an `expr` reference
|
|
145
|
+
// (`invoiceData?.Invoice?.hasShipping`) compared with the STRING operand
|
|
146
|
+
// 'true'. The renderer resolves that expr to boolean true, so a strict
|
|
147
|
+
// `===` against the operand can never pass — the row was hidden on every
|
|
148
|
+
// invoice that had a fee.
|
|
149
|
+
const textNode = (children: unknown[]) => ({
|
|
150
|
+
type: 'element',
|
|
151
|
+
content: { elementType: 'text', semanticType: 'p', children },
|
|
152
|
+
})
|
|
153
|
+
const templateUidl = {
|
|
154
|
+
name: 'invoice-template',
|
|
155
|
+
node: {
|
|
156
|
+
type: 'element',
|
|
157
|
+
content: {
|
|
158
|
+
elementType: 'container',
|
|
159
|
+
children: [
|
|
160
|
+
{
|
|
161
|
+
type: 'conditional',
|
|
162
|
+
content: {
|
|
163
|
+
reference: { type: 'expr', content: 'invoiceData?.Invoice?.hasShipping' },
|
|
164
|
+
condition: { conditions: [{ operation: '=', operand: 'true' }] },
|
|
165
|
+
node: {
|
|
166
|
+
type: 'element',
|
|
167
|
+
content: {
|
|
168
|
+
elementType: 'container',
|
|
169
|
+
name: 'Delivery Row',
|
|
170
|
+
children: [
|
|
171
|
+
textNode([{ type: 'static', content: 'Delivery:' }]),
|
|
172
|
+
textNode([{ type: 'expr', content: 'invoiceData?.Invoice?.shippingAmount' }]),
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
const code = generatePdfGeneratorCode(FAKE_SETTINGS, templateUidl, {})
|
|
183
|
+
const moduleObject = { exports: {} as Record<string, unknown> }
|
|
184
|
+
new Function('require', 'module', 'exports', `${code}; return module.exports;`)(
|
|
185
|
+
require,
|
|
186
|
+
moduleObject,
|
|
187
|
+
moduleObject.exports
|
|
188
|
+
)
|
|
189
|
+
const templated = moduleObject.exports as unknown as PdfGeneratorModule
|
|
190
|
+
|
|
191
|
+
const withFee = templated.buildInvoiceHtml({
|
|
192
|
+
...baseInvoice,
|
|
193
|
+
shippingAmount: 9.99,
|
|
194
|
+
total: 209.99,
|
|
195
|
+
})
|
|
196
|
+
expect(withFee).toContain('Delivery:')
|
|
197
|
+
expect(withFee).toContain('$9.99')
|
|
198
|
+
|
|
199
|
+
const withoutFee = templated.buildInvoiceHtml({ ...baseInvoice, total: 200 })
|
|
200
|
+
expect(withoutFee).not.toContain('Delivery:')
|
|
201
|
+
})
|
|
202
|
+
|
|
143
203
|
it('fills the {{shippingAmount}} merge token in the invoice email', () => {
|
|
144
204
|
const data = pdfGenerator.buildDataContext({
|
|
145
205
|
...baseInvoice,
|
|
@@ -153,3 +213,47 @@ describe('invoice PDF renderer — delivery fee', () => {
|
|
|
153
213
|
expect(pdfGenerator.replacePlaceholders('Due: {{totalAmount}}', data)).toBe('Due: $209.99')
|
|
154
214
|
})
|
|
155
215
|
})
|
|
216
|
+
|
|
217
|
+
describe('invoice PDF renderer — each line says which VAT rate it was taxed at', () => {
|
|
218
|
+
const pdfGenerator = loadPdfGenerator()
|
|
219
|
+
const regionalInvoice = {
|
|
220
|
+
invoiceNumber: 'INV-0001',
|
|
221
|
+
taxRate: 20,
|
|
222
|
+
taxIncludedInPrice: false,
|
|
223
|
+
subtotal: 247,
|
|
224
|
+
taxAmount: 40,
|
|
225
|
+
shippingAmount: 0,
|
|
226
|
+
total: 287,
|
|
227
|
+
currency: 'RON',
|
|
228
|
+
items: [
|
|
229
|
+
{
|
|
230
|
+
name: 'Kettle',
|
|
231
|
+
quantity: 2,
|
|
232
|
+
unitPrice: 100,
|
|
233
|
+
totalPrice: 200,
|
|
234
|
+
taxRate: 20,
|
|
235
|
+
taxIncluded: false,
|
|
236
|
+
},
|
|
237
|
+
{ name: 'Book', quantity: 1, unitPrice: 47, totalPrice: 47, taxRate: 9, taxIncluded: true },
|
|
238
|
+
],
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
it('binds the per-line rate and VAT beside the net price', () => {
|
|
242
|
+
const products = (
|
|
243
|
+
pdfGenerator.buildInvoiceDataScope(regionalInvoice).invoiceData as unknown as {
|
|
244
|
+
Products: Array<Record<string, string>>
|
|
245
|
+
}
|
|
246
|
+
).Products
|
|
247
|
+
expect(products.map((product) => product.lineVatRate)).toEqual(['20%', '9%'])
|
|
248
|
+
expect(products[0].lineVatAmount).toContain('40.00')
|
|
249
|
+
expect(products[1].lineVatAmount).toContain('3.88')
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('prints the VAT % and VAT columns and a VAT total on the fallback invoice', () => {
|
|
253
|
+
const html = pdfGenerator.buildInvoiceHtml(regionalInvoice)
|
|
254
|
+
expect(html).toContain('>VAT %</th>')
|
|
255
|
+
expect(html).toContain('>20%</td>')
|
|
256
|
+
expect(html).toContain('>9%</td>')
|
|
257
|
+
expect(html).toContain('<span>VAT</span>')
|
|
258
|
+
})
|
|
259
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* tslint:disable:function-constructor */
|
|
2
|
+
import { generateInvoiceGenerateRouteCode } from '../src/invoice/api-routes-code'
|
|
3
|
+
import { REGIONAL_INVOICE_TAX_CODE } from '../src/invoice/regional-invoice-tax-code'
|
|
4
|
+
import type { UIDLInvoiceSettings } from '@teleporthq/teleport-types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The invoice of an order priced by region (shipping zones + tax
|
|
8
|
+
* jurisdictions) is computed from the rates the order was CHARGED at —
|
|
9
|
+
* `teleport_orders.tax_breakdown` — not from the store's single default rate,
|
|
10
|
+
* and its total must equal `goods + shipping_amount - discount_amount`, the
|
|
11
|
+
* amount the buyer was debited.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface RegionalInvoiceTax {
|
|
15
|
+
taxRate: number
|
|
16
|
+
taxIncluded: boolean
|
|
17
|
+
subtotal: number
|
|
18
|
+
taxAmount: number
|
|
19
|
+
shippingNet: number
|
|
20
|
+
total: number
|
|
21
|
+
itemTax: Array<{ rate: number; included: boolean; amount: number }>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const api = new Function(
|
|
25
|
+
REGIONAL_INVOICE_TAX_CODE +
|
|
26
|
+
'\nreturn { parse: parseOrderTaxBreakdown, resolve: resolveRegionalInvoiceTax };'
|
|
27
|
+
)() as {
|
|
28
|
+
parse: (raw: unknown) => Record<string, unknown> | null
|
|
29
|
+
resolve: (
|
|
30
|
+
breakdown: Record<string, unknown>,
|
|
31
|
+
items: Array<Record<string, unknown>>,
|
|
32
|
+
discountAmount: number,
|
|
33
|
+
shippingAmount: number
|
|
34
|
+
) => RegionalInvoiceTax
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cents = (n: number) => Math.round(n * 100) / 100
|
|
38
|
+
|
|
39
|
+
// Germany: food at 7% and tools at 19%, both added on top; 6.00 shipping taxed
|
|
40
|
+
// at 19% → shipping_amount 7.14 on the order.
|
|
41
|
+
const GERMAN_ORDER = {
|
|
42
|
+
taxRate: 19,
|
|
43
|
+
taxIncluded: false,
|
|
44
|
+
taxDecimals: 2,
|
|
45
|
+
shippingTax: 1.14,
|
|
46
|
+
lines: [
|
|
47
|
+
{ productId: 'p1', variantId: '', taxRate: 7, taxIncluded: false },
|
|
48
|
+
{ productId: 'p2', variantId: 'v2', taxRate: 19, taxIncluded: false },
|
|
49
|
+
],
|
|
50
|
+
}
|
|
51
|
+
const GERMAN_ITEMS = [
|
|
52
|
+
{ productId: 'p1', variantId: null, quantity: 2, unitPrice: 10, totalPrice: 20 },
|
|
53
|
+
{ productId: 'p2', variantId: 'v2', quantity: 1, unitPrice: 25.99, totalPrice: 25.99 },
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
describe('invoice — regional tax breakdown', () => {
|
|
57
|
+
it('reads the breakdown from the TEXT column, and nothing from a legacy order', () => {
|
|
58
|
+
expect(api.parse(JSON.stringify(GERMAN_ORDER))).toMatchObject({ taxRate: 19 })
|
|
59
|
+
expect(api.parse(null)).toBeNull()
|
|
60
|
+
expect(api.parse('not json')).toBeNull()
|
|
61
|
+
expect(api.parse('{"taxRate":19}')).toBeNull()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('taxes each line at its own rate and reconciles with the order total', () => {
|
|
65
|
+
const result = api.resolve(GERMAN_ORDER, GERMAN_ITEMS, 0, 7.14)
|
|
66
|
+
// goods: 21.40 + 30.93 = 52.33, tax 1.40 + 4.94 = 6.34; shipping tax 1.14.
|
|
67
|
+
expect(cents(result.total)).toBe(59.47)
|
|
68
|
+
expect(cents(result.taxAmount)).toBe(7.48)
|
|
69
|
+
expect(cents(result.subtotal)).toBe(45.99)
|
|
70
|
+
// Delivery prints net, so Subtotal + Tax + Delivery is the total.
|
|
71
|
+
expect(cents(result.shippingNet)).toBe(6)
|
|
72
|
+
expect(cents(result.subtotal + result.taxAmount + result.shippingNet)).toBe(59.47)
|
|
73
|
+
expect(result.itemTax).toEqual([
|
|
74
|
+
{ rate: 7, included: false, amount: 1.4 },
|
|
75
|
+
{ rate: 19, included: false, amount: 4.94 },
|
|
76
|
+
])
|
|
77
|
+
expect(result.taxRate).toBe(19)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('takes a discount off the goods first, then the shipping, shrinking each tax in proportion', () => {
|
|
81
|
+
const goodsOnly = api.resolve(GERMAN_ORDER, GERMAN_ITEMS, 10.466, 7.14)
|
|
82
|
+
// 52.33 - 10.466 leaves 80% of the goods: 6.34 * 0.8 = 5.072, shipping untouched.
|
|
83
|
+
expect(cents(goodsOnly.total)).toBe(cents(52.33 - 10.466 + 7.14))
|
|
84
|
+
expect(cents(goodsOnly.taxAmount)).toBe(cents(6.34 * 0.8 + 1.14))
|
|
85
|
+
|
|
86
|
+
const intoShipping = api.resolve(GERMAN_ORDER, GERMAN_ITEMS, 55.9, 7.14)
|
|
87
|
+
// The goods are fully discounted and 3.57 of 7.14 shipping is left: half its tax.
|
|
88
|
+
expect(cents(intoShipping.total)).toBe(3.57)
|
|
89
|
+
expect(cents(intoShipping.taxAmount)).toBe(0.57)
|
|
90
|
+
expect(intoShipping.subtotal).toBe(0)
|
|
91
|
+
expect(cents(intoShipping.shippingNet)).toBe(3)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('extracts included tax without adding it, and falls back to the standard rate for an unmatched line', () => {
|
|
95
|
+
const french = api.resolve(
|
|
96
|
+
{ taxRate: 20, taxIncluded: true, taxDecimals: 2, shippingTax: 1, lines: [] },
|
|
97
|
+
[{ productId: 'p9', quantity: 1, unitPrice: 25.99, totalPrice: 25.99 }],
|
|
98
|
+
0,
|
|
99
|
+
6
|
|
100
|
+
)
|
|
101
|
+
expect(cents(french.total)).toBe(31.99)
|
|
102
|
+
expect(cents(french.taxAmount)).toBe(5.33)
|
|
103
|
+
expect(french.itemTax).toEqual([{ rate: 20, included: true, amount: 4.33 }])
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('/api/invoices/generate — regional tax wiring', () => {
|
|
108
|
+
const route = generateInvoiceGenerateRouteCode({
|
|
109
|
+
invoicePrefix: 'INV-',
|
|
110
|
+
defaultTaxRate: 20,
|
|
111
|
+
showDiscount: false,
|
|
112
|
+
taxIncludedInPrice: false,
|
|
113
|
+
companyDetails: {},
|
|
114
|
+
template: { document: null },
|
|
115
|
+
tables: { invoicesTable: 'teleport_invoices', invoiceItemsTable: 'teleport_invoice_items' },
|
|
116
|
+
emailDelivery: { enabled: false },
|
|
117
|
+
} as unknown as UIDLInvoiceSettings)
|
|
118
|
+
|
|
119
|
+
it('prices from the order breakdown unless the caller names its own rate', () => {
|
|
120
|
+
expect(route).toContain('function resolveRegionalInvoiceTax(')
|
|
121
|
+
expect(route).toContain('var regionalTaxBreakdown = body.taxRate == null')
|
|
122
|
+
expect(route).toContain('parseOrderTaxBreakdown(orderShippingSource.tax_breakdown)')
|
|
123
|
+
expect(route).toContain('taxIncludedInPrice: taxIncludedInPrice,')
|
|
124
|
+
expect(route).toContain('variantId: row.variant_id || row.variantId || null,')
|
|
125
|
+
})
|
|
126
|
+
})
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { NextWorkflowProjectPlugin } from '../src/workflow-project-plugin'
|
|
2
|
+
import {
|
|
3
|
+
generateCronAPIRoute,
|
|
4
|
+
generateServerSegmentAPIRoute,
|
|
5
|
+
generateStreamingServerSegmentAPIRoute,
|
|
6
|
+
generateWebhookWorkflowAPIRoute,
|
|
7
|
+
} from '../src/api-route-generator'
|
|
8
|
+
import { generateClientRuntimeCode } from '../src/executor-generator'
|
|
9
|
+
import { paymentChargeUser } from '../src/nodes/payment/payment-charge-user'
|
|
10
|
+
import {
|
|
11
|
+
WORKFLOW_UTILS_ALIAS,
|
|
12
|
+
workflowUtilsAliasLine,
|
|
13
|
+
workflowUtilsImportLine,
|
|
14
|
+
} from '../src/workflow-utils-alias'
|
|
15
|
+
import { WorkflowSegment } from '../src/types'
|
|
16
|
+
import { loadHandler, HandlerFn } from './_helpers/load-handler'
|
|
17
|
+
import { bindWorkflowUtils } from './_helpers/bind-workflow-utils'
|
|
18
|
+
import { compileGeneratedMiddleware } from './_helpers/run-generated-middleware'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A visitor who switched the site to Spanish must stay on the Spanish site
|
|
22
|
+
* through every redirect the generated app performs: the account handlers'
|
|
23
|
+
* "back to the home page", the navigation nodes, the payment provider's return
|
|
24
|
+
* URLs and the middleware's sign-in redirect. Next.js serves the non-default
|
|
25
|
+
* languages under a "/<locale>" prefix, so each of those has to put the run's
|
|
26
|
+
* (or the request's) locale back on the path it builds. Executed, not grepped:
|
|
27
|
+
* what matters is where the browser is sent.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
interface BrowserStub {
|
|
31
|
+
location: { href: string }
|
|
32
|
+
open: jest.Mock
|
|
33
|
+
localStorage: { setItem: jest.Mock; removeItem: jest.Mock }
|
|
34
|
+
dispatchEvent: jest.Mock
|
|
35
|
+
setTimeout: (fn: () => void, ms: number) => void
|
|
36
|
+
document: { createElement: () => Record<string, unknown>; body: { appendChild: jest.Mock } }
|
|
37
|
+
__teleportNextAuth: Record<string, jest.Mock>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const installBrowser = (): BrowserStub => {
|
|
41
|
+
const element = { textContent: '', setAttribute: jest.fn() }
|
|
42
|
+
const browser: BrowserStub = {
|
|
43
|
+
location: { href: '' },
|
|
44
|
+
open: jest.fn(),
|
|
45
|
+
localStorage: { setItem: jest.fn(), removeItem: jest.fn() },
|
|
46
|
+
dispatchEvent: jest.fn(),
|
|
47
|
+
// The farewell redirect is delayed so its toast is readable; run it now.
|
|
48
|
+
setTimeout: (fn) => fn(),
|
|
49
|
+
document: { createElement: () => element, body: { appendChild: jest.fn() } },
|
|
50
|
+
__teleportNextAuth: {
|
|
51
|
+
signIn: jest.fn(async () => ({})),
|
|
52
|
+
signOut: jest.fn(async () => undefined),
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
;(globalThis as any).window = browser
|
|
56
|
+
;(globalThis as any).CustomEvent = class {
|
|
57
|
+
public type: string
|
|
58
|
+
public detail: unknown
|
|
59
|
+
constructor(type: string, init?: { detail?: unknown }) {
|
|
60
|
+
this.type = type
|
|
61
|
+
this.detail = init ? init.detail : undefined
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return browser
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const okJson = (payload: unknown) => async () => ({ ok: true, json: async () => payload })
|
|
68
|
+
|
|
69
|
+
describe('client handlers keep the language of the run', () => {
|
|
70
|
+
let unbindWorkflowUtils: () => void
|
|
71
|
+
let browser: BrowserStub
|
|
72
|
+
|
|
73
|
+
beforeAll(() => {
|
|
74
|
+
unbindWorkflowUtils = bindWorkflowUtils()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
afterAll(() => {
|
|
78
|
+
unbindWorkflowUtils()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
browser = installBrowser()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
delete (globalThis as any).window
|
|
87
|
+
delete (globalThis as any).CustomEvent
|
|
88
|
+
delete (globalThis as any).fetch
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('account-login lands on the home page of the language signed in from', async () => {
|
|
92
|
+
const handler: HandlerFn = loadHandler('account-login')
|
|
93
|
+
;(globalThis as any).fetch = okJson({ user: { id: 'u-1' } })
|
|
94
|
+
await handler({ email: 'a@b.c', password: 'secret' }, { __locale: 'es' })
|
|
95
|
+
expect(browser.location.href).toBe('/es')
|
|
96
|
+
await handler({ email: 'a@b.c', password: 'secret' }, { __locale: 'en' })
|
|
97
|
+
expect(browser.location.href).toBe('/')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('account-signup lands on the home page of the language signed up from', async () => {
|
|
101
|
+
const handler: HandlerFn = loadHandler('account-signup')
|
|
102
|
+
;(globalThis as any).fetch = okJson({ user: { id: 'u-1' } })
|
|
103
|
+
await handler({ email: 'a@b.c', password: 'secret' }, { __locale: 'es' })
|
|
104
|
+
expect(browser.location.href).toBe('/es')
|
|
105
|
+
await handler({ email: 'a@b.c', password: 'secret' }, {})
|
|
106
|
+
expect(browser.location.href).toBe('/')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('account-logout lands on the home page of the language signed out from', async () => {
|
|
110
|
+
const handler: HandlerFn = loadHandler('account-logout')
|
|
111
|
+
await handler({}, { __locale: 'es' })
|
|
112
|
+
expect(browser.location.href).toBe('/es')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('account-delete-current lands on the configured page in the language deleted from', async () => {
|
|
116
|
+
const handler: HandlerFn = loadHandler('account-delete-current')
|
|
117
|
+
;(globalThis as any).fetch = okJson({ success: true })
|
|
118
|
+
await handler({ redirectTo: '/goodbye' }, { __locale: 'es' })
|
|
119
|
+
expect(browser.location.href).toBe('/es/goodbye')
|
|
120
|
+
await handler({}, { __locale: 'es' })
|
|
121
|
+
expect(browser.location.href).toBe('/es')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('account-social-login asks the provider to return to the language started from', async () => {
|
|
125
|
+
const handler: HandlerFn = loadHandler('account-social-login')
|
|
126
|
+
await handler({ provider: 'google' }, { __locale: 'es' })
|
|
127
|
+
expect(browser.__teleportNextAuth.signIn).toHaveBeenCalledWith('google', {
|
|
128
|
+
callbackUrl: '/es',
|
|
129
|
+
})
|
|
130
|
+
await handler({ provider: 'google', callbackUrl: '/my-profile' }, { __locale: 'es' })
|
|
131
|
+
expect(browser.__teleportNextAuth.signIn).toHaveBeenLastCalledWith('google', {
|
|
132
|
+
callbackUrl: '/es/my-profile',
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('navigation-navigate-to-url localizes a site path and leaves everything else alone', async () => {
|
|
137
|
+
const handler: HandlerFn = loadHandler('navigation-navigate-to-url')
|
|
138
|
+
await handler({ url: '/sign-in' }, { __locale: 'es' })
|
|
139
|
+
expect(browser.location.href).toBe('/es/sign-in')
|
|
140
|
+
await handler({ url: '/sign-in', openInNewTab: true }, { __locale: 'es' })
|
|
141
|
+
expect(browser.open).toHaveBeenCalledWith('/es/sign-in', '_blank')
|
|
142
|
+
await handler({ url: 'https://example.com/x' }, { __locale: 'es' })
|
|
143
|
+
expect(browser.location.href).toBe('https://example.com/x')
|
|
144
|
+
await handler({ url: '/api/invoices/download?id=1' }, { __locale: 'es' })
|
|
145
|
+
expect(browser.location.href).toBe('/api/invoices/download?id=1')
|
|
146
|
+
await handler({ url: '/sign-in' }, {})
|
|
147
|
+
expect(browser.location.href).toBe('/sign-in')
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('payment-charge-user returns the buyer to the language of the checkout', () => {
|
|
152
|
+
let unbindWorkflowUtils: () => void
|
|
153
|
+
|
|
154
|
+
beforeAll(() => {
|
|
155
|
+
unbindWorkflowUtils = bindWorkflowUtils()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
afterAll(() => {
|
|
159
|
+
unbindWorkflowUtils()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const source = paymentChargeUser.generateHandler()
|
|
163
|
+
// The entry and the helpers up to the real absolutizer; the provider calls
|
|
164
|
+
// are stubbed to capture the URLs they were handed.
|
|
165
|
+
const entrySource = source.slice(
|
|
166
|
+
source.indexOf('async function payment_charge_user'),
|
|
167
|
+
source.indexOf('function toStripeMinorUnits')
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const charge = async (
|
|
171
|
+
config: Record<string, unknown>,
|
|
172
|
+
context: Record<string, unknown>
|
|
173
|
+
): Promise<Record<string, unknown>> => {
|
|
174
|
+
const capture = async (
|
|
175
|
+
_config: unknown,
|
|
176
|
+
_currency: unknown,
|
|
177
|
+
_amount: unknown,
|
|
178
|
+
successUrl: string,
|
|
179
|
+
cancelUrl: string
|
|
180
|
+
) => ({ successUrl, cancelUrl })
|
|
181
|
+
// eslint-disable-next-line no-new-func
|
|
182
|
+
const factory = new Function(
|
|
183
|
+
'chargeWithStripe',
|
|
184
|
+
'chargeWithPaypal',
|
|
185
|
+
`${entrySource}\nreturn payment_charge_user;`
|
|
186
|
+
)
|
|
187
|
+
return factory(capture, capture)(config, context)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
it('prefixes site-relative return paths with the run locale before absolutizing', async () => {
|
|
191
|
+
const result = await charge(
|
|
192
|
+
{ successUrl: '/orders/ORD-42?payment=success', cancelUrl: '/checkout', amount: 10 },
|
|
193
|
+
{ __baseUrl: 'https://shop.test', __locale: 'es' }
|
|
194
|
+
)
|
|
195
|
+
expect(result).toEqual({
|
|
196
|
+
successUrl: 'https://shop.test/es/orders/ORD-42?payment=success',
|
|
197
|
+
cancelUrl: 'https://shop.test/es/checkout',
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('keeps the bare paths for the default language and an external override untouched', async () => {
|
|
202
|
+
expect(
|
|
203
|
+
await charge(
|
|
204
|
+
{ successUrl: '/orders/ORD-42', cancelUrl: 'https://pay.example/back', amount: 10 },
|
|
205
|
+
{ __baseUrl: 'https://shop.test', __locale: 'en' }
|
|
206
|
+
)
|
|
207
|
+
).toEqual({
|
|
208
|
+
successUrl: 'https://shop.test/orders/ORD-42',
|
|
209
|
+
cancelUrl: 'https://pay.example/back',
|
|
210
|
+
})
|
|
211
|
+
expect(
|
|
212
|
+
await charge({ successUrl: '/orders/ORD-42', amount: 10 }, { __baseUrl: 'https://shop.test' })
|
|
213
|
+
).toMatchObject({ successUrl: 'https://shop.test/orders/ORD-42' })
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
describe('every file that inlines handlers binds the shared runtime alias', () => {
|
|
218
|
+
const segment: WorkflowSegment = {
|
|
219
|
+
id: 'seg-1',
|
|
220
|
+
env: 'server',
|
|
221
|
+
nodeIds: ['n1'],
|
|
222
|
+
nodes: [
|
|
223
|
+
{
|
|
224
|
+
id: 'n1',
|
|
225
|
+
type: 'payment-charge-user',
|
|
226
|
+
config: { amount: 10 },
|
|
227
|
+
executionEnv: 'server',
|
|
228
|
+
stepNumber: 1,
|
|
229
|
+
label: 'Charge',
|
|
230
|
+
} as any,
|
|
231
|
+
],
|
|
232
|
+
edges: [],
|
|
233
|
+
}
|
|
234
|
+
const workflow = {
|
|
235
|
+
id: 'wf-1',
|
|
236
|
+
name: 'WF',
|
|
237
|
+
nodes: [segment.nodes[0]],
|
|
238
|
+
edges: [],
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
it('api routes alias the runtime they already require', () => {
|
|
242
|
+
const routes = [
|
|
243
|
+
generateServerSegmentAPIRoute(segment, 'wf'),
|
|
244
|
+
generateStreamingServerSegmentAPIRoute(segment, 'wf'),
|
|
245
|
+
generateCronAPIRoute({
|
|
246
|
+
...workflow,
|
|
247
|
+
trigger: {
|
|
248
|
+
type: 'event-cron-triggered',
|
|
249
|
+
nodeId: 't',
|
|
250
|
+
scope: 'global',
|
|
251
|
+
config: { schedule: '0 * * * *', urlPath: 'cron_x' },
|
|
252
|
+
},
|
|
253
|
+
} as any),
|
|
254
|
+
generateWebhookWorkflowAPIRoute({
|
|
255
|
+
...workflow,
|
|
256
|
+
trigger: { type: 'event-webhook-triggered', nodeId: 't', scope: 'global', config: {} },
|
|
257
|
+
webhookConfig: { urlPath: 'hooks/x', httpMethod: 'POST', signatureSecret: '' },
|
|
258
|
+
} as any),
|
|
259
|
+
]
|
|
260
|
+
for (const route of routes) {
|
|
261
|
+
const aliasAt = route.indexOf(workflowUtilsAliasLine('utils'))
|
|
262
|
+
expect(aliasAt).toBeGreaterThan(route.indexOf('server-runtime'))
|
|
263
|
+
expect(aliasAt).toBeLessThan(route.indexOf('const nodeHandlers = {'))
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('the global workflows hook imports the runtime as an ES module', () => {
|
|
268
|
+
const plugin = new NextWorkflowProjectPlugin()
|
|
269
|
+
const hook: string = (plugin as any).generateGlobalWorkflowsHook([
|
|
270
|
+
{
|
|
271
|
+
id: 'global-wf',
|
|
272
|
+
trigger: { type: 'event-custom-triggered', config: { eventName: 'x' } },
|
|
273
|
+
nodes: [
|
|
274
|
+
{
|
|
275
|
+
id: 'n1',
|
|
276
|
+
type: 'navigation-navigate-to-url',
|
|
277
|
+
config: { url: '/sign-in' },
|
|
278
|
+
stepNumber: 1,
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
edges: [],
|
|
282
|
+
},
|
|
283
|
+
])
|
|
284
|
+
expect(hook).toContain(workflowUtilsImportLine('./runtime-utils'))
|
|
285
|
+
// Pure ESM, like the runtime import beside it (see the hook's emitter).
|
|
286
|
+
expect(hook).not.toContain(`${WORKFLOW_UTILS_ALIAS} = require(`)
|
|
287
|
+
expect(hook.indexOf(workflowUtilsImportLine('./runtime-utils'))).toBeLessThan(
|
|
288
|
+
hook.indexOf('export function useGlobalWorkflows')
|
|
289
|
+
)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('the client runtime localizes a terminal redirect it performs itself', () => {
|
|
293
|
+
expect(generateClientRuntimeCode()).toContain(
|
|
294
|
+
'window.location.href = utils.localizeHref(__redirectUrl, context.__locale);'
|
|
295
|
+
)
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
describe('middleware redirects keep the request locale', () => {
|
|
300
|
+
const auth: any = {
|
|
301
|
+
enabled: true,
|
|
302
|
+
dataSourceId: 'ds-1',
|
|
303
|
+
dataSourceType: 'postgres',
|
|
304
|
+
passwordAuthEnabled: true,
|
|
305
|
+
providers: [],
|
|
306
|
+
roles: ['admin'],
|
|
307
|
+
tables: {},
|
|
308
|
+
folderProtection: {},
|
|
309
|
+
authPages: {
|
|
310
|
+
signIn: { pageId: 'sign-in', pageName: 'sign-in', route: '/sign-in' },
|
|
311
|
+
signUp: { pageId: 'sign-up', pageName: 'sign-up', route: '/sign-up' },
|
|
312
|
+
},
|
|
313
|
+
pageProtection: {
|
|
314
|
+
ORDERS: { requiresAuth: true, allowedRoles: [], pageName: 'Orders', route: '/orders' },
|
|
315
|
+
ADMIN: {
|
|
316
|
+
requiresAuth: true,
|
|
317
|
+
allowedRoles: ['admin'],
|
|
318
|
+
pageName: 'Admin',
|
|
319
|
+
route: '/admin',
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
callbackBaseUrl: '',
|
|
323
|
+
envKeys: {},
|
|
324
|
+
customUserProperties: [],
|
|
325
|
+
}
|
|
326
|
+
const run = compileGeneratedMiddleware(auth)
|
|
327
|
+
|
|
328
|
+
it('sends a guest to the sign-in page of the language they were browsing in', async () => {
|
|
329
|
+
const result = await run('/orders', { locale: 'es', defaultLocale: 'en' })
|
|
330
|
+
expect(result).toEqual({
|
|
331
|
+
kind: 'redirect',
|
|
332
|
+
location: 'https://shop.test/es/sign-in?callbackUrl=%2Fes%2Forders',
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('keeps the bare routes for the default language and for a project without languages', async () => {
|
|
337
|
+
expect(await run('/orders', { locale: 'en', defaultLocale: 'en' })).toEqual({
|
|
338
|
+
kind: 'redirect',
|
|
339
|
+
location: 'https://shop.test/sign-in?callbackUrl=%2Forders',
|
|
340
|
+
})
|
|
341
|
+
expect(await run('/orders')).toEqual({
|
|
342
|
+
kind: 'redirect',
|
|
343
|
+
location: 'https://shop.test/sign-in?callbackUrl=%2Forders',
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('sends a signed-in visitor without the role to the home page of their language', async () => {
|
|
348
|
+
const result = await run('/admin', {
|
|
349
|
+
locale: 'es',
|
|
350
|
+
defaultLocale: 'en',
|
|
351
|
+
sessionUser: { id: 'u-1', role: 'user' },
|
|
352
|
+
})
|
|
353
|
+
expect(result).toEqual({ kind: 'redirect', location: 'https://shop.test/es' })
|
|
354
|
+
})
|
|
355
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* tslint:disable:no-eval */
|
|
2
2
|
import { navigationGoToPage } from '../src/nodes/navigation/navigation-go-to-page'
|
|
3
|
+
import { bindWorkflowUtils } from './_helpers/bind-workflow-utils'
|
|
3
4
|
|
|
4
5
|
function createHandler() {
|
|
5
6
|
const fnStr = navigationGoToPage.generateHandler()
|
|
@@ -23,11 +24,17 @@ function withWindow(): FakeWindow {
|
|
|
23
24
|
describe('navigation-go-to-page', () => {
|
|
24
25
|
let handler: any
|
|
25
26
|
let win: FakeWindow
|
|
27
|
+
let unbindWorkflowUtils: () => void
|
|
26
28
|
|
|
27
29
|
beforeAll(() => {
|
|
30
|
+
unbindWorkflowUtils = bindWorkflowUtils()
|
|
28
31
|
handler = createHandler()
|
|
29
32
|
})
|
|
30
33
|
|
|
34
|
+
afterAll(() => {
|
|
35
|
+
unbindWorkflowUtils()
|
|
36
|
+
})
|
|
37
|
+
|
|
31
38
|
beforeEach(() => {
|
|
32
39
|
win = withWindow()
|
|
33
40
|
})
|
|
@@ -36,6 +43,42 @@ describe('navigation-go-to-page', () => {
|
|
|
36
43
|
delete (global as any).window
|
|
37
44
|
})
|
|
38
45
|
|
|
46
|
+
describe('language of the run', () => {
|
|
47
|
+
// The visitor stays in the language they are browsing in: the run's locale
|
|
48
|
+
// (put on the context by the runtime) prefixes every site-relative target.
|
|
49
|
+
it('prefixes the target with a non-default locale', async () => {
|
|
50
|
+
await handler({ pageId: '/about', openInNewTab: false }, { __locale: 'es' })
|
|
51
|
+
expect(win.location.href).toBe('/es/about')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('prefixes the home page, a details page and its query params alike', async () => {
|
|
55
|
+
await handler({ pageId: '/', openInNewTab: false }, { __locale: 'es' })
|
|
56
|
+
expect(win.location.href).toBe('/es')
|
|
57
|
+
await handler(
|
|
58
|
+
{
|
|
59
|
+
openInNewTab: false,
|
|
60
|
+
differentiator: 'ORD-42',
|
|
61
|
+
targetPage: { pageId: 'p', staticUrl: '/orders', isDetailsPage: true },
|
|
62
|
+
queryParams: [{ key: 'payment', value: 'success' }],
|
|
63
|
+
},
|
|
64
|
+
{ __locale: 'es' }
|
|
65
|
+
)
|
|
66
|
+
expect(win.location.href).toBe('/es/orders/ORD-42?payment=success')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('opens the localized target in a new tab when requested', async () => {
|
|
70
|
+
await handler({ pageId: '/about', openInNewTab: true }, { __locale: 'es' })
|
|
71
|
+
expect(win.open).toHaveBeenCalledWith('/es/about', '_blank')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('keeps the bare route for the default language and for a run without a locale', async () => {
|
|
75
|
+
await handler({ pageId: '/about', openInNewTab: false }, { __locale: 'en' })
|
|
76
|
+
expect(win.location.href).toBe('/about')
|
|
77
|
+
await handler({ pageId: '/about', openInNewTab: false }, {})
|
|
78
|
+
expect(win.location.href).toBe('/about')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
39
82
|
describe('legacy UIDL (no targetPage)', () => {
|
|
40
83
|
it('navigates to the rewritten pageId', async () => {
|
|
41
84
|
const result = await handler({ pageId: '/about', openInNewTab: false }, {})
|