@teleporthq/teleport-plugin-next-workflows 0.43.75 → 0.44.1
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/fake-store-database.ts +428 -0
- package/__tests__/_helpers/invoice-store.ts +382 -0
- package/__tests__/booking-calendar-feed-route.test.ts +494 -0
- package/__tests__/custom-node-result-taken-branch.test.ts +107 -0
- package/__tests__/data-api-date-parser.test.ts +45 -0
- package/__tests__/email-locale-senders.test.ts +3 -1
- package/__tests__/email-sender-name.test.ts +513 -0
- package/__tests__/graph-utils-topological-order.test.ts +155 -1
- package/__tests__/invoice-billing-details.test.ts +384 -0
- package/__tests__/invoice-data-access-replace.test.ts +236 -2
- package/__tests__/invoice-order-items-settle.test.ts +1 -1
- package/__tests__/invoice-payment-email.test.ts +191 -0
- package/__tests__/invoice-regenerate-email.test.ts +288 -0
- package/__tests__/invoice-regenerate-route.test.ts +9 -7
- package/__tests__/invoice-reservation.test.ts +1 -1
- package/__tests__/invoice-tender.test.ts +1 -1
- package/__tests__/middleware-admin-panel-roles.test.ts +127 -0
- package/__tests__/page-arrival-remount.test.ts +247 -0
- package/__tests__/payment-drivers-module.test.ts +19 -0
- package/__tests__/raw-query-error-code.test.ts +47 -0
- package/__tests__/runtime-template-token-resolution.test.ts +16 -0
- package/__tests__/segment-reply-changed-keys.test.ts +31 -0
- package/__tests__/segment-route-skipped-branch.test.ts +101 -0
- package/__tests__/segment-splitter-checkout-order.test.ts +306 -0
- package/__tests__/streaming-on-end-env-filter.test.ts +18 -18
- package/__tests__/streaming-on-end-order.test.ts +387 -0
- package/dist/cjs/account-delete-route-generator.d.ts +1 -0
- package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
- package/dist/cjs/account-delete-route-generator.js +2 -1
- 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 +23 -13
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/auth-generator.d.ts +1 -0
- package/dist/cjs/auth-generator.d.ts.map +1 -1
- package/dist/cjs/auth-generator.js +77 -28
- package/dist/cjs/auth-generator.js.map +1 -1
- package/dist/cjs/booking-calendar-feed-route-generator.d.ts +28 -0
- package/dist/cjs/booking-calendar-feed-route-generator.d.ts.map +1 -0
- package/dist/cjs/booking-calendar-feed-route-generator.js +296 -0
- package/dist/cjs/booking-calendar-feed-route-generator.js.map +1 -0
- package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
- package/dist/cjs/data-api-route-generator.js +6 -1
- package/dist/cjs/data-api-route-generator.js.map +1 -1
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +39 -28
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/graph-utils.d.ts +31 -1
- package/dist/cjs/graph-utils.d.ts.map +1 -1
- package/dist/cjs/graph-utils.js +112 -10
- package/dist/cjs/graph-utils.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/invoice/api-routes-code.d.ts +12 -3
- package/dist/cjs/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/cjs/invoice/api-routes-code.js +152 -21
- package/dist/cjs/invoice/api-routes-code.js.map +1 -1
- package/dist/cjs/invoice/data-access-code.d.ts.map +1 -1
- package/dist/cjs/invoice/data-access-code.js +133 -21
- package/dist/cjs/invoice/data-access-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 +15 -25
- package/dist/cjs/invoice/email-sender-code.js.map +1 -1
- package/dist/cjs/invoice/index.js +1 -1
- package/dist/cjs/invoice/index.js.map +1 -1
- package/dist/cjs/invoice/invoice-assembly-code.d.ts.map +1 -1
- package/dist/cjs/invoice/invoice-assembly-code.js +50 -6
- package/dist/cjs/invoice/invoice-assembly-code.js.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.js +13 -2
- package/dist/cjs/invoice/invoice-html-code.js.map +1 -1
- package/dist/cjs/nodes/data/data-raw-query.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-raw-query.js +11 -1
- package/dist/cjs/nodes/data/data-raw-query.js.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.js +4 -2
- package/dist/cjs/nodes/email/email-mailgun.js.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.js +5 -2
- package/dist/cjs/nodes/email/email-postmark.js.map +1 -1
- package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-resend.js +5 -2
- package/dist/cjs/nodes/email/email-resend.js.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.js +8 -2
- package/dist/cjs/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/cjs/nodes/general/general-custom-js.d.ts.map +1 -1
- package/dist/cjs/nodes/general/general-custom-js.js +11 -0
- package/dist/cjs/nodes/general/general-custom-js.js.map +1 -1
- package/dist/cjs/nodes/storage/storage-local-remove.d.ts.map +1 -1
- package/dist/cjs/nodes/storage/storage-local-remove.js +2 -0
- package/dist/cjs/nodes/storage/storage-local-remove.js.map +1 -1
- package/dist/cjs/nodes/storage/storage-local-set.d.ts.map +1 -1
- package/dist/cjs/nodes/storage/storage-local-set.js +4 -0
- package/dist/cjs/nodes/storage/storage-local-set.js.map +1 -1
- package/dist/cjs/page-arrival.d.ts +10 -0
- package/dist/cjs/page-arrival.d.ts.map +1 -0
- package/dist/cjs/page-arrival.js +86 -0
- package/dist/cjs/page-arrival.js.map +1 -0
- package/dist/cjs/payments/index.d.ts +10 -4
- package/dist/cjs/payments/index.d.ts.map +1 -1
- package/dist/cjs/payments/index.js +18 -7
- package/dist/cjs/payments/index.js.map +1 -1
- package/dist/cjs/segment-splitter.d.ts.map +1 -1
- package/dist/cjs/segment-splitter.js +65 -27
- package/dist/cjs/segment-splitter.js.map +1 -1
- package/dist/cjs/transactional-email-code.d.ts.map +1 -1
- package/dist/cjs/transactional-email-code.js +32 -18
- package/dist/cjs/transactional-email-code.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/workflow-component-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-component-plugin.js +35 -149
- 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 +16 -4
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/cjs/workflow-scope.d.ts +19 -0
- package/dist/cjs/workflow-scope.d.ts.map +1 -0
- package/dist/cjs/workflow-scope.js +147 -0
- package/dist/cjs/workflow-scope.js.map +1 -0
- package/dist/esm/account-delete-route-generator.d.ts +1 -0
- package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
- package/dist/esm/account-delete-route-generator.js +2 -1
- 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 +23 -13
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/auth-generator.d.ts +1 -0
- package/dist/esm/auth-generator.d.ts.map +1 -1
- package/dist/esm/auth-generator.js +77 -28
- package/dist/esm/auth-generator.js.map +1 -1
- package/dist/esm/booking-calendar-feed-route-generator.d.ts +28 -0
- package/dist/esm/booking-calendar-feed-route-generator.d.ts.map +1 -0
- package/dist/esm/booking-calendar-feed-route-generator.js +291 -0
- package/dist/esm/booking-calendar-feed-route-generator.js.map +1 -0
- package/dist/esm/data-api-route-generator.d.ts.map +1 -1
- package/dist/esm/data-api-route-generator.js +6 -1
- package/dist/esm/data-api-route-generator.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +39 -28
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/graph-utils.d.ts +31 -1
- package/dist/esm/graph-utils.d.ts.map +1 -1
- package/dist/esm/graph-utils.js +109 -9
- package/dist/esm/graph-utils.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/invoice/api-routes-code.d.ts +12 -3
- package/dist/esm/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/esm/invoice/api-routes-code.js +152 -21
- package/dist/esm/invoice/api-routes-code.js.map +1 -1
- package/dist/esm/invoice/data-access-code.d.ts.map +1 -1
- package/dist/esm/invoice/data-access-code.js +133 -21
- package/dist/esm/invoice/data-access-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 +15 -25
- package/dist/esm/invoice/email-sender-code.js.map +1 -1
- package/dist/esm/invoice/index.js +1 -1
- package/dist/esm/invoice/index.js.map +1 -1
- package/dist/esm/invoice/invoice-assembly-code.d.ts.map +1 -1
- package/dist/esm/invoice/invoice-assembly-code.js +50 -6
- package/dist/esm/invoice/invoice-assembly-code.js.map +1 -1
- package/dist/esm/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/esm/invoice/invoice-html-code.js +13 -2
- package/dist/esm/invoice/invoice-html-code.js.map +1 -1
- package/dist/esm/nodes/data/data-raw-query.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-raw-query.js +11 -1
- package/dist/esm/nodes/data/data-raw-query.js.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.js +4 -2
- package/dist/esm/nodes/email/email-mailgun.js.map +1 -1
- package/dist/esm/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-postmark.js +5 -2
- package/dist/esm/nodes/email/email-postmark.js.map +1 -1
- package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-resend.js +5 -2
- package/dist/esm/nodes/email/email-resend.js.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.js +8 -2
- package/dist/esm/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/esm/nodes/general/general-custom-js.d.ts.map +1 -1
- package/dist/esm/nodes/general/general-custom-js.js +11 -0
- package/dist/esm/nodes/general/general-custom-js.js.map +1 -1
- package/dist/esm/nodes/storage/storage-local-remove.d.ts.map +1 -1
- package/dist/esm/nodes/storage/storage-local-remove.js +2 -0
- package/dist/esm/nodes/storage/storage-local-remove.js.map +1 -1
- package/dist/esm/nodes/storage/storage-local-set.d.ts.map +1 -1
- package/dist/esm/nodes/storage/storage-local-set.js +4 -0
- package/dist/esm/nodes/storage/storage-local-set.js.map +1 -1
- package/dist/esm/page-arrival.d.ts +10 -0
- package/dist/esm/page-arrival.d.ts.map +1 -0
- package/dist/esm/page-arrival.js +81 -0
- package/dist/esm/page-arrival.js.map +1 -0
- package/dist/esm/payments/index.d.ts +10 -4
- package/dist/esm/payments/index.d.ts.map +1 -1
- package/dist/esm/payments/index.js +16 -6
- package/dist/esm/payments/index.js.map +1 -1
- package/dist/esm/segment-splitter.d.ts.map +1 -1
- package/dist/esm/segment-splitter.js +66 -28
- package/dist/esm/segment-splitter.js.map +1 -1
- package/dist/esm/transactional-email-code.d.ts.map +1 -1
- package/dist/esm/transactional-email-code.js +32 -18
- package/dist/esm/transactional-email-code.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/workflow-component-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-component-plugin.js +32 -146
- 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 +16 -4
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/dist/esm/workflow-scope.d.ts +19 -0
- package/dist/esm/workflow-scope.d.ts.map +1 -0
- package/dist/esm/workflow-scope.js +142 -0
- package/dist/esm/workflow-scope.js.map +1 -0
- package/package.json +4 -4
- package/src/account-delete-route-generator.ts +3 -1
- package/src/api-route-generator.ts +23 -13
- package/src/auth-generator.ts +83 -26
- package/src/booking-calendar-feed-route-generator.ts +293 -0
- package/src/data-api-route-generator.ts +6 -1
- package/src/executor-generator.ts +39 -28
- package/src/graph-utils.ts +143 -11
- package/src/index.ts +1 -0
- package/src/invoice/api-routes-code.ts +166 -21
- package/src/invoice/data-access-code.ts +139 -21
- package/src/invoice/email-sender-code.ts +15 -25
- package/src/invoice/index.ts +1 -1
- package/src/invoice/invoice-assembly-code.ts +50 -6
- package/src/invoice/invoice-html-code.ts +13 -2
- package/src/nodes/data/data-raw-query.ts +11 -1
- package/src/nodes/email/email-mailgun.ts +11 -2
- package/src/nodes/email/email-postmark.ts +12 -2
- package/src/nodes/email/email-resend.ts +12 -2
- package/src/nodes/email/email-sendgrid.ts +15 -2
- package/src/nodes/general/general-custom-js.ts +10 -0
- package/src/nodes/storage/storage-local-remove.ts +4 -0
- package/src/nodes/storage/storage-local-set.ts +6 -0
- package/src/page-arrival.ts +115 -0
- package/src/payments/index.ts +18 -5
- package/src/segment-splitter.ts +90 -36
- package/src/transactional-email-code.ts +32 -21
- package/src/workflow-component-plugin.ts +43 -188
- package/src/workflow-project-plugin.ts +17 -4
- package/src/workflow-scope.ts +184 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// A page keeps its instance when a navigation lands on the page already on
|
|
2
|
+
// screen (a link to the current URL, another record of the same dynamic route,
|
|
3
|
+
// another query string), so a trigger that fires when the page appears —
|
|
4
|
+
// page-loaded, element-visible, component-loaded — ran only on the first
|
|
5
|
+
// arrival. Such pages are wrapped in `remountOnPageArrival`, which starts them
|
|
6
|
+
// fresh on every arrival; pages whose workflows only answer interactions keep
|
|
7
|
+
// their plain export.
|
|
8
|
+
|
|
9
|
+
import generator from '@babel/generator'
|
|
10
|
+
import * as types from '@babel/types'
|
|
11
|
+
import { createNextWorkflowPlugin } from '../src/workflow-component-plugin'
|
|
12
|
+
|
|
13
|
+
const PAGE_ID = 'page-dashboard'
|
|
14
|
+
const ROOT_ID = 'thq_container_root'
|
|
15
|
+
|
|
16
|
+
const workflow = (type: string, scope: string, config: Record<string, unknown>) => ({
|
|
17
|
+
id: `wf-${type}`,
|
|
18
|
+
name: type,
|
|
19
|
+
trigger: { type, nodeId: 'trigger-1', scope, config },
|
|
20
|
+
nodes: [
|
|
21
|
+
{
|
|
22
|
+
id: 'update-1',
|
|
23
|
+
type: 'state-update-local-state',
|
|
24
|
+
config: { property: 'isLoading', value: false },
|
|
25
|
+
stepNumber: 1,
|
|
26
|
+
label: 'Update',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
edges: [{ id: 'e1', source: 'trigger-1', target: 'update-1' }],
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const jsxComponentChunk = (rootId: string) => ({
|
|
33
|
+
type: 'AST',
|
|
34
|
+
name: 'jsx-component',
|
|
35
|
+
content: types.variableDeclaration('const', [
|
|
36
|
+
types.variableDeclarator(
|
|
37
|
+
types.identifier('Dashboard'),
|
|
38
|
+
types.arrowFunctionExpression(
|
|
39
|
+
[types.identifier('props')],
|
|
40
|
+
types.blockStatement([
|
|
41
|
+
types.returnStatement(
|
|
42
|
+
types.jsxElement(
|
|
43
|
+
types.jsxOpeningElement(types.jsxIdentifier('div'), [
|
|
44
|
+
types.jsxAttribute(types.jsxIdentifier('id'), types.stringLiteral(rootId)),
|
|
45
|
+
]),
|
|
46
|
+
types.jsxClosingElement(types.jsxIdentifier('div')),
|
|
47
|
+
[]
|
|
48
|
+
)
|
|
49
|
+
),
|
|
50
|
+
])
|
|
51
|
+
)
|
|
52
|
+
),
|
|
53
|
+
]),
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
interface RunOptions {
|
|
57
|
+
isPage?: boolean
|
|
58
|
+
rootId?: string
|
|
59
|
+
pageNode?: unknown
|
|
60
|
+
projectComponents?: Record<string, unknown>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const run = async (
|
|
64
|
+
workflows: Array<ReturnType<typeof workflow>>,
|
|
65
|
+
{
|
|
66
|
+
isPage = true,
|
|
67
|
+
rootId = ROOT_ID,
|
|
68
|
+
pageNode = { type: 'element', content: { elementType: 'container' } },
|
|
69
|
+
projectComponents,
|
|
70
|
+
}: RunOptions = {}
|
|
71
|
+
) => {
|
|
72
|
+
const structure: any = {
|
|
73
|
+
uidl: {
|
|
74
|
+
name: 'Dashboard',
|
|
75
|
+
node: pageNode,
|
|
76
|
+
stateDefinitions: { isLoading: { type: 'boolean', defaultValue: true } },
|
|
77
|
+
outputOptions: { pageId: PAGE_ID, folderPath: [] },
|
|
78
|
+
},
|
|
79
|
+
chunks: [
|
|
80
|
+
jsxComponentChunk(rootId),
|
|
81
|
+
{
|
|
82
|
+
type: 'AST',
|
|
83
|
+
name: 'export',
|
|
84
|
+
content: types.exportDefaultDeclaration(types.identifier('Dashboard')),
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
options: {
|
|
88
|
+
workflows: {
|
|
89
|
+
workflows: workflows.reduce(
|
|
90
|
+
(byId: Record<string, unknown>, wf) => ({ ...byId, [wf.id]: wf }),
|
|
91
|
+
{}
|
|
92
|
+
),
|
|
93
|
+
customNodes: {},
|
|
94
|
+
},
|
|
95
|
+
pagesPath: ['pages'],
|
|
96
|
+
extractedResources: {},
|
|
97
|
+
projectComponents,
|
|
98
|
+
},
|
|
99
|
+
dependencies: {},
|
|
100
|
+
}
|
|
101
|
+
await createNextWorkflowPlugin({ isPage })(structure)
|
|
102
|
+
const exportChunk = structure.chunks.find((chunk: any) => chunk.name === 'export')
|
|
103
|
+
const moduleChunk = structure.chunks.find((chunk: any) => chunk.name === 'workflow-module')
|
|
104
|
+
return {
|
|
105
|
+
exportCode: generator(exportChunk.content).code,
|
|
106
|
+
moduleCode: moduleChunk ? String(moduleChunk.content) : '',
|
|
107
|
+
dependencies: structure.dependencies,
|
|
108
|
+
extractedResources: structure.options.extractedResources,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const BANNER_ID = 'thq_container_banner'
|
|
113
|
+
|
|
114
|
+
/** An instance of a project component, as local dependencies are resolved for pages. */
|
|
115
|
+
const componentInstance = (name: string) => ({
|
|
116
|
+
type: 'element',
|
|
117
|
+
content: { elementType: 'component', semanticType: name, dependency: { type: 'local' } },
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const pageRendering = (componentName: string) => ({
|
|
121
|
+
type: 'element',
|
|
122
|
+
content: { elementType: 'container', children: [componentInstance(componentName)] },
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const bannerComponent = () => ({
|
|
126
|
+
name: 'Banner',
|
|
127
|
+
node: {
|
|
128
|
+
type: 'element',
|
|
129
|
+
content: { elementType: 'container', attrs: { id: { type: 'static', content: BANNER_ID } } },
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const WRAPPED_EXPORT = 'export default remountOnPageArrival(Dashboard);'
|
|
134
|
+
const PLAIN_EXPORT = 'export default Dashboard;'
|
|
135
|
+
|
|
136
|
+
describe('workflow plugin — remount on page arrival', () => {
|
|
137
|
+
it('wraps a page whose workflow runs when the page loads', async () => {
|
|
138
|
+
const result = await run([workflow('event-page-loaded', 'page', { pageId: PAGE_ID })])
|
|
139
|
+
|
|
140
|
+
expect(result.exportCode).toBe(WRAPPED_EXPORT)
|
|
141
|
+
expect(result.dependencies.remountOnPageArrival).toEqual({
|
|
142
|
+
type: 'local',
|
|
143
|
+
path: '../utils/page-arrival',
|
|
144
|
+
})
|
|
145
|
+
expect(result.extractedResources['utils/page-arrival']).toBeDefined()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('wraps a page whose workflow runs when its root element becomes visible', async () => {
|
|
149
|
+
const result = await run([
|
|
150
|
+
workflow('event-element-visible', 'element', { elementHtmlId: ROOT_ID, once: true }),
|
|
151
|
+
])
|
|
152
|
+
|
|
153
|
+
expect(result.exportCode).toBe(WRAPPED_EXPORT)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('leaves the page alone when the visible element is rendered elsewhere', async () => {
|
|
157
|
+
const result = await run(
|
|
158
|
+
[workflow('event-element-visible', 'element', { elementHtmlId: ROOT_ID, once: true })],
|
|
159
|
+
{ rootId: 'thq_container_other' }
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
expect(result.exportCode).toBe(PLAIN_EXPORT)
|
|
163
|
+
expect(result.dependencies.remountOnPageArrival).toBeUndefined()
|
|
164
|
+
expect(result.extractedResources).toEqual({})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('leaves the page alone when its workflows only answer interactions', async () => {
|
|
168
|
+
const result = await run([
|
|
169
|
+
workflow('event-element-clicked', 'element', { elementHtmlId: ROOT_ID }),
|
|
170
|
+
workflow('event-interval', 'page', { pageId: PAGE_ID, intervalMs: 1000 }),
|
|
171
|
+
])
|
|
172
|
+
|
|
173
|
+
expect(result.moduleCode).not.toBe('')
|
|
174
|
+
expect(result.exportCode).toBe(PLAIN_EXPORT)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('never wraps a component', async () => {
|
|
178
|
+
const result = await run(
|
|
179
|
+
[workflow('event-component-loaded', 'component', { componentName: 'Dashboard' })],
|
|
180
|
+
{ isPage: false }
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
expect(result.moduleCode).toContain('Component loaded')
|
|
184
|
+
expect(result.exportCode).toBe(PLAIN_EXPORT)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('wraps a page that renders a component running a workflow when its element is visible', async () => {
|
|
188
|
+
const result = await run(
|
|
189
|
+
[workflow('event-element-visible', 'element', { elementHtmlId: BANNER_ID, once: true })],
|
|
190
|
+
{ pageNode: pageRendering('Banner'), projectComponents: { Banner: bannerComponent() } }
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
expect(result.moduleCode).toBe('')
|
|
194
|
+
expect(result.exportCode).toBe(WRAPPED_EXPORT)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('finds the component through the components it is nested in', async () => {
|
|
198
|
+
const result = await run(
|
|
199
|
+
[workflow('event-element-visible', 'element', { elementHtmlId: BANNER_ID, once: true })],
|
|
200
|
+
{
|
|
201
|
+
pageNode: pageRendering('Layout'),
|
|
202
|
+
projectComponents: {
|
|
203
|
+
Layout: { name: 'Layout', node: componentInstance('Banner') },
|
|
204
|
+
Banner: bannerComponent(),
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
expect(result.exportCode).toBe(WRAPPED_EXPORT)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('wraps a page that renders a component running a workflow when it loads', async () => {
|
|
213
|
+
const result = await run(
|
|
214
|
+
[workflow('event-component-loaded', 'component', { componentName: 'Banner' })],
|
|
215
|
+
{ pageNode: pageRendering('Banner'), projectComponents: { Banner: bannerComponent() } }
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
expect(result.exportCode).toBe(WRAPPED_EXPORT)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('leaves the page alone when no rendered component observes the element', async () => {
|
|
222
|
+
const result = await run(
|
|
223
|
+
[
|
|
224
|
+
workflow('event-element-visible', 'element', {
|
|
225
|
+
elementHtmlId: 'thq_elsewhere',
|
|
226
|
+
once: true,
|
|
227
|
+
}),
|
|
228
|
+
],
|
|
229
|
+
{ pageNode: pageRendering('Banner'), projectComponents: { Banner: bannerComponent() } }
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
expect(result.exportCode).toBe(PLAIN_EXPORT)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('cancels a delayed page-loaded run together with the other triggers', async () => {
|
|
236
|
+
const result = await run([
|
|
237
|
+
workflow('event-page-loaded', 'page', { pageId: PAGE_ID, delay: 500 }),
|
|
238
|
+
])
|
|
239
|
+
|
|
240
|
+
expect(result.moduleCode).toContain(
|
|
241
|
+
'const __delay_wf_event_page_loaded = setTimeout(function() { __execWf(__wfConfig_wf_event_page_loaded'
|
|
242
|
+
)
|
|
243
|
+
expect(result.moduleCode).toContain(
|
|
244
|
+
'cleanups.push(function() { clearTimeout(__delay_wf_event_page_loaded); });'
|
|
245
|
+
)
|
|
246
|
+
})
|
|
247
|
+
})
|
|
@@ -163,6 +163,25 @@ describe('writing the module', () => {
|
|
|
163
163
|
)
|
|
164
164
|
})
|
|
165
165
|
|
|
166
|
+
it('reads the providers of a site paid without a store, never both blocks at once', () => {
|
|
167
|
+
const paymentSettings = {
|
|
168
|
+
paymentProviders: [
|
|
169
|
+
{ type: 'mollie', credentials: { apiKey: 'CONFIGURATION_MOLLIE_API_KEY' } },
|
|
170
|
+
],
|
|
171
|
+
}
|
|
172
|
+
expect(readDeclaredPaymentCredentials({ paymentSettings } as any)).toEqual({
|
|
173
|
+
mollie: { apiKey: 'CONFIGURATION_MOLLIE_API_KEY' },
|
|
174
|
+
})
|
|
175
|
+
expect(resolveStorePaymentDriverIds({ paymentSettings } as any, [])).toContain('mollie')
|
|
176
|
+
// A store's own providers win: the editor never exports both.
|
|
177
|
+
expect(
|
|
178
|
+
readDeclaredPaymentCredentials({
|
|
179
|
+
paymentSettings,
|
|
180
|
+
ecommerceSettings: { paymentProviders: [] },
|
|
181
|
+
} as any)
|
|
182
|
+
).toEqual({})
|
|
183
|
+
})
|
|
184
|
+
|
|
166
185
|
it('writes nothing for a store that takes no online payment', () => {
|
|
167
186
|
const target = structure()
|
|
168
187
|
ensurePaymentDriversModule(target as any, [])
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { loadHandler } from './_helpers/load-handler'
|
|
2
|
+
|
|
3
|
+
// A statement that raises on purpose (a seat race lost, a slot taken) is told
|
|
4
|
+
// apart from a broken one by the SQLSTATE the data route answers with — the
|
|
5
|
+
// route keeps the database's message in its own log, so the code is all a
|
|
6
|
+
// workflow can read.
|
|
7
|
+
|
|
8
|
+
async function runAgainst(response: { ok: boolean; body: unknown }): Promise<any> {
|
|
9
|
+
const fetchMock = jest.fn(async () => ({
|
|
10
|
+
ok: response.ok,
|
|
11
|
+
json: async () => response.body,
|
|
12
|
+
}))
|
|
13
|
+
const originalFetch = (globalThis as any).fetch
|
|
14
|
+
;(globalThis as any).fetch = fetchMock
|
|
15
|
+
try {
|
|
16
|
+
const handler = loadHandler('data-raw-query')
|
|
17
|
+
return await handler({ dataSourceId: 'ds', query: 'SELECT 1', params: [] }, {})
|
|
18
|
+
} finally {
|
|
19
|
+
;(globalThis as any).fetch = originalFetch
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('data-raw-query — a database error reaches the workflow with its SQLSTATE', () => {
|
|
24
|
+
it('passes the route’s code on beside the error', async () => {
|
|
25
|
+
const result = await runAgainst({
|
|
26
|
+
ok: false,
|
|
27
|
+
body: { error: 'Internal server error', code: '2203A' },
|
|
28
|
+
})
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
rows: [],
|
|
31
|
+
result: [],
|
|
32
|
+
error: 'Internal server error',
|
|
33
|
+
code: '2203A',
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('answers no code when the route gave none', async () => {
|
|
38
|
+
const result = await runAgainst({ ok: false, body: { error: 'INVALID_SQL_IDENTIFIER' } })
|
|
39
|
+
expect(result).toEqual({ rows: [], result: [], error: 'INVALID_SQL_IDENTIFIER' })
|
|
40
|
+
expect('code' in result).toBe(false)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('answers the rows of a statement that went through', async () => {
|
|
44
|
+
const result = await runAgainst({ ok: true, body: { rows: [{ code: 'ok' }] } })
|
|
45
|
+
expect(result).toEqual({ rows: [{ code: 'ok' }], result: [{ code: 'ok' }] })
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -132,6 +132,22 @@ describe('workflow runtime template-token resolution', () => {
|
|
|
132
132
|
expect(cfg.columnMappings[0].value).toBeNull()
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
+
it('never walks into a live element or around a cycle (a trigger target in a custom-js param)', () => {
|
|
136
|
+
const fiber: Record<string, unknown> = {}
|
|
137
|
+
const element: Record<string, unknown> = {
|
|
138
|
+
nodeType: 1,
|
|
139
|
+
nodeName: 'INPUT',
|
|
140
|
+
__reactFiber$abc: fiber,
|
|
141
|
+
}
|
|
142
|
+
fiber.stateNode = element
|
|
143
|
+
const loop: Record<string, unknown> = { a: 1 }
|
|
144
|
+
loop.self = loop
|
|
145
|
+
const cfg = { code: 'x', params: { event: { target: element, value: SENTINEL }, loop } }
|
|
146
|
+
expect(utils.finalizeResolvedConfig('general-custom-js', cfg)).toBeNull()
|
|
147
|
+
expect(cfg.params.event.value).toBeNull()
|
|
148
|
+
expect(cfg.params.event.target).toBe(element)
|
|
149
|
+
})
|
|
150
|
+
|
|
135
151
|
it('leaves configs without sentinels untouched', () => {
|
|
136
152
|
const cfg = { filters: [{ field: 'id', value: null, operator: '=' }] }
|
|
137
153
|
expect(utils.finalizeResolvedConfig('data-select', cfg)).toBeNull()
|
|
@@ -111,6 +111,37 @@ describe('generated server segment — what it replies with', () => {
|
|
|
111
111
|
expect(results).not.toHaveProperty('__pendingNodePromises')
|
|
112
112
|
})
|
|
113
113
|
|
|
114
|
+
it('keeps a result marked keepResultOnServer on the server, for the nodes after it only', async () => {
|
|
115
|
+
const kept: WorkflowSegment = {
|
|
116
|
+
...segment,
|
|
117
|
+
nodes: [
|
|
118
|
+
{ ...calcNode, config: { ...calcNode.config, keepResultOnServer: true } },
|
|
119
|
+
gateNode,
|
|
120
|
+
bigNode,
|
|
121
|
+
],
|
|
122
|
+
}
|
|
123
|
+
const results = (await bootRoute(kept)(request())).body.results || {}
|
|
124
|
+
expect(results).not.toHaveProperty('calc')
|
|
125
|
+
// The gate read it on the server: 42 is not above 100.
|
|
126
|
+
expect(results.gate).toEqual({ result: false })
|
|
127
|
+
expect(results.__skippedNodes).toEqual({ big: true })
|
|
128
|
+
// Nothing is signed for a result that never left.
|
|
129
|
+
expect((results.__sig as Record<string, string>).calc).toBeUndefined()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('still replies with a branching node marked keepResultOnServer: the client branches on it', async () => {
|
|
133
|
+
const kept: WorkflowSegment = {
|
|
134
|
+
...segment,
|
|
135
|
+
nodes: [
|
|
136
|
+
calcNode,
|
|
137
|
+
{ ...gateNode, config: { ...gateNode.config, keepResultOnServer: true } },
|
|
138
|
+
bigNode,
|
|
139
|
+
],
|
|
140
|
+
}
|
|
141
|
+
const results = (await bootRoute(kept)(request())).body.results || {}
|
|
142
|
+
expect(results.gate).toEqual({ result: false })
|
|
143
|
+
})
|
|
144
|
+
|
|
114
145
|
it('still replies with what the route itself adds to the run', async () => {
|
|
115
146
|
const results = (await bootRoute(segment)(request())).body.results || {}
|
|
116
147
|
// The route learns the deployment's origin; a custom node called next
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { generateServerSegmentAPIRoute } from '../src/api-route-generator'
|
|
2
|
+
import type { WorkflowSegment } from '../src/types'
|
|
3
|
+
import { loadServerRuntime } from './_helpers/load-server-runtime'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A server segment never runs a node of an if-statement branch it did not
|
|
7
|
+
* take — an if-statement there included. It used to evaluate those before
|
|
8
|
+
* checking the skip list, and hand their `{ result }`s back: a custom node
|
|
9
|
+
* answers the output of the last node it ran, so the email senders of
|
|
10
|
+
* teleport-gui (one "is it this email?" → send → "was it accepted?" → report
|
|
11
|
+
* branch per purpose, all in one server segment) answered `{ result: false }`
|
|
12
|
+
* instead of their report, and every caller read "not sent" whatever happened.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const purpose = { type: 'workflowContext', nodeId: 'read', path: ['read', 'purpose'] }
|
|
16
|
+
|
|
17
|
+
const ifNode = (id: string, stepNumber: number, leftValue: unknown, rightValue: unknown) => ({
|
|
18
|
+
id,
|
|
19
|
+
type: 'general-if-statement',
|
|
20
|
+
label: id,
|
|
21
|
+
config: { conditionType: 'simple-comparison', leftValue, operator: '===', rightValue },
|
|
22
|
+
executionEnv: 'server',
|
|
23
|
+
stepNumber,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const segment = {
|
|
27
|
+
id: 'server-1',
|
|
28
|
+
env: 'server',
|
|
29
|
+
nodeIds: ['isA', 'acceptedA', 'isB', 'acceptedB'],
|
|
30
|
+
nodes: [
|
|
31
|
+
ifNode('isA', 1, purpose, 'a'),
|
|
32
|
+
ifNode('acceptedA', 2, 'yes', 'yes'),
|
|
33
|
+
ifNode('isB', 2, purpose, 'b'),
|
|
34
|
+
// Numbered after the branch that runs: the one an answer used to pick.
|
|
35
|
+
ifNode('acceptedB', 3, 'yes', 'yes'),
|
|
36
|
+
],
|
|
37
|
+
edges: [
|
|
38
|
+
{ id: 'e1', source: 'isA', target: 'acceptedA', sourceHandle: 'true' },
|
|
39
|
+
{ id: 'e2', source: 'isA', target: 'isB', sourceHandle: 'false' },
|
|
40
|
+
{ id: 'e3', source: 'isB', target: 'acceptedB', sourceHandle: 'true' },
|
|
41
|
+
],
|
|
42
|
+
} as unknown as WorkflowSegment
|
|
43
|
+
|
|
44
|
+
async function runSegment(incoming: Record<string, unknown>): Promise<Record<string, any>> {
|
|
45
|
+
const runtime = loadServerRuntime()
|
|
46
|
+
const requireShim = (id: string) => {
|
|
47
|
+
if (id.endsWith('/utils/workflows/server-runtime')) {
|
|
48
|
+
return runtime
|
|
49
|
+
}
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
51
|
+
return require(id)
|
|
52
|
+
}
|
|
53
|
+
const module = { exports: null as any }
|
|
54
|
+
// eslint-disable-next-line no-new-func
|
|
55
|
+
new Function('module', 'exports', 'require', generateServerSegmentAPIRoute(segment, 'Sender'))(
|
|
56
|
+
module,
|
|
57
|
+
{},
|
|
58
|
+
requireShim
|
|
59
|
+
)
|
|
60
|
+
const reply = { status: 0, body: null as any }
|
|
61
|
+
const res = {
|
|
62
|
+
status(code: number) {
|
|
63
|
+
reply.status = code
|
|
64
|
+
return {
|
|
65
|
+
json(payload: unknown) {
|
|
66
|
+
reply.body = payload
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
setHeader: () => undefined,
|
|
71
|
+
}
|
|
72
|
+
await module.exports(
|
|
73
|
+
{
|
|
74
|
+
method: 'POST',
|
|
75
|
+
url: '/api/workflows/sender-seg-1',
|
|
76
|
+
headers: { host: 'shop.test', 'x-forwarded-proto': 'https' },
|
|
77
|
+
body: { context: incoming },
|
|
78
|
+
},
|
|
79
|
+
res
|
|
80
|
+
)
|
|
81
|
+
expect(reply.status).toBe(200)
|
|
82
|
+
return reply.body.results
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe('generated server segment route — a branch not taken', () => {
|
|
86
|
+
it('answers the branch it took, and nothing of the one it skipped', async () => {
|
|
87
|
+
const results = await runSegment({ read: { purpose: 'a' } })
|
|
88
|
+
expect(results.isA).toEqual({ result: true })
|
|
89
|
+
expect(results.acceptedA).toEqual({ result: true })
|
|
90
|
+
expect(results).not.toHaveProperty('isB')
|
|
91
|
+
expect(results).not.toHaveProperty('acceptedB')
|
|
92
|
+
expect(results.__skippedNodes).toMatchObject({ isB: true, acceptedB: true })
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('runs the other branch when that one is taken', async () => {
|
|
96
|
+
const results = await runSegment({ read: { purpose: 'b' } })
|
|
97
|
+
expect(results.isB).toEqual({ result: true })
|
|
98
|
+
expect(results.acceptedB).toEqual({ result: true })
|
|
99
|
+
expect(results).not.toHaveProperty('acceptedA')
|
|
100
|
+
})
|
|
101
|
+
})
|