@teleporthq/teleport-plugin-next-data-source 0.43.67 → 0.43.68

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.
Files changed (202) hide show
  1. package/__tests__/array-overlap-sql.test.ts +53 -0
  2. package/__tests__/blog-post-page-enrichment.test.ts +214 -0
  3. package/__tests__/browser-row-policy.test.ts +503 -0
  4. package/__tests__/data-source-cache-wrapping.test.ts +18 -2
  5. package/__tests__/ecommerce-product-options.test.ts +206 -0
  6. package/__tests__/ecommerce-product-pages.test.ts +82 -0
  7. package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
  8. package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
  9. package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
  10. package/__tests__/fetchers.test.ts +2 -2
  11. package/__tests__/items-path.test.ts +150 -0
  12. package/__tests__/product-options-storefront.fixture.json +2510 -0
  13. package/__tests__/product-page-url-fixtures.json +25 -0
  14. package/__tests__/product-price-sort.ts +4 -2
  15. package/__tests__/raw-rows-mode.test.ts +111 -0
  16. package/__tests__/rest-api-response-cache.test.ts +434 -0
  17. package/__tests__/table-read-route-protected-tables.test.ts +493 -0
  18. package/dist/cjs/array-overlap-sql.d.ts +21 -0
  19. package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
  20. package/dist/cjs/array-overlap-sql.js +25 -0
  21. package/dist/cjs/array-overlap-sql.js.map +1 -0
  22. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  23. package/dist/cjs/data-source-fetchers.js +10 -4
  24. package/dist/cjs/data-source-fetchers.js.map +1 -1
  25. package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
  26. package/dist/cjs/fetchers/javascript.js +1 -1
  27. package/dist/cjs/fetchers/javascript.js.map +1 -1
  28. package/dist/cjs/fetchers/postgresql.d.ts +1 -1
  29. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  30. package/dist/cjs/fetchers/postgresql.js +8 -4
  31. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  32. package/dist/cjs/fetchers/raw-query.d.ts +8 -1
  33. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
  34. package/dist/cjs/fetchers/raw-query.js +42 -4
  35. package/dist/cjs/fetchers/raw-query.js.map +1 -1
  36. package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
  37. package/dist/cjs/fetchers/rest-api.js +7 -2
  38. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  39. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  40. package/dist/cjs/fetchers/teleport.js +8 -5
  41. package/dist/cjs/fetchers/teleport.js.map +1 -1
  42. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
  43. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  44. package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
  45. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
  46. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
  47. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  48. package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
  49. package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
  50. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  51. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  52. package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
  53. package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
  54. package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
  55. package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
  56. package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
  57. package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
  58. package/dist/cjs/items-path.d.ts +10 -0
  59. package/dist/cjs/items-path.d.ts.map +1 -0
  60. package/dist/cjs/items-path.js +86 -0
  61. package/dist/cjs/items-path.js.map +1 -0
  62. package/dist/cjs/localized-columns.d.ts.map +1 -1
  63. package/dist/cjs/localized-columns.js +13 -0
  64. package/dist/cjs/localized-columns.js.map +1 -1
  65. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  66. package/dist/cjs/pagination-plugin.js +17 -5
  67. package/dist/cjs/pagination-plugin.js.map +1 -1
  68. package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
  69. package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
  70. package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
  71. package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
  72. package/dist/cjs/transformations/blog-comments.d.ts +30 -0
  73. package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
  74. package/dist/cjs/transformations/blog-comments.js +34 -0
  75. package/dist/cjs/transformations/blog-comments.js.map +1 -0
  76. package/dist/cjs/transformations/blog-post.d.ts +2 -0
  77. package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
  78. package/dist/cjs/transformations/blog-post.js +4 -1
  79. package/dist/cjs/transformations/blog-post.js.map +1 -1
  80. package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
  81. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
  82. package/dist/cjs/transformations/ecommerce-product.js +28 -1
  83. package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
  84. package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
  85. package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
  86. package/dist/cjs/transformations/heading-anchors.js +29 -0
  87. package/dist/cjs/transformations/heading-anchors.js.map +1 -0
  88. package/dist/cjs/transformations/index.d.ts +47 -4
  89. package/dist/cjs/transformations/index.d.ts.map +1 -1
  90. package/dist/cjs/transformations/index.js +56 -18
  91. package/dist/cjs/transformations/index.js.map +1 -1
  92. package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
  93. package/dist/cjs/transformations/shared-utils.js +1 -1
  94. package/dist/cjs/transformations/shared-utils.js.map +1 -1
  95. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  96. package/dist/cjs/utils.d.ts.map +1 -1
  97. package/dist/cjs/utils.js +1 -1
  98. package/dist/cjs/utils.js.map +1 -1
  99. package/dist/esm/array-overlap-sql.d.ts +21 -0
  100. package/dist/esm/array-overlap-sql.d.ts.map +1 -0
  101. package/dist/esm/array-overlap-sql.js +21 -0
  102. package/dist/esm/array-overlap-sql.js.map +1 -0
  103. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  104. package/dist/esm/data-source-fetchers.js +11 -5
  105. package/dist/esm/data-source-fetchers.js.map +1 -1
  106. package/dist/esm/fetchers/javascript.d.ts.map +1 -1
  107. package/dist/esm/fetchers/javascript.js +1 -1
  108. package/dist/esm/fetchers/javascript.js.map +1 -1
  109. package/dist/esm/fetchers/postgresql.d.ts +1 -1
  110. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  111. package/dist/esm/fetchers/postgresql.js +8 -4
  112. package/dist/esm/fetchers/postgresql.js.map +1 -1
  113. package/dist/esm/fetchers/raw-query.d.ts +8 -1
  114. package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
  115. package/dist/esm/fetchers/raw-query.js +42 -4
  116. package/dist/esm/fetchers/raw-query.js.map +1 -1
  117. package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
  118. package/dist/esm/fetchers/rest-api.js +7 -2
  119. package/dist/esm/fetchers/rest-api.js.map +1 -1
  120. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  121. package/dist/esm/fetchers/teleport.js +8 -5
  122. package/dist/esm/fetchers/teleport.js.map +1 -1
  123. package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
  124. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  125. package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
  126. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
  127. package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
  128. package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  129. package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
  130. package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
  131. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  132. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  133. package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
  134. package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
  135. package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
  136. package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
  137. package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
  138. package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
  139. package/dist/esm/items-path.d.ts +10 -0
  140. package/dist/esm/items-path.d.ts.map +1 -0
  141. package/dist/esm/items-path.js +58 -0
  142. package/dist/esm/items-path.js.map +1 -0
  143. package/dist/esm/localized-columns.d.ts.map +1 -1
  144. package/dist/esm/localized-columns.js +13 -0
  145. package/dist/esm/localized-columns.js.map +1 -1
  146. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  147. package/dist/esm/pagination-plugin.js +17 -5
  148. package/dist/esm/pagination-plugin.js.map +1 -1
  149. package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
  150. package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
  151. package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
  152. package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
  153. package/dist/esm/transformations/blog-comments.d.ts +30 -0
  154. package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
  155. package/dist/esm/transformations/blog-comments.js +30 -0
  156. package/dist/esm/transformations/blog-comments.js.map +1 -0
  157. package/dist/esm/transformations/blog-post.d.ts +2 -0
  158. package/dist/esm/transformations/blog-post.d.ts.map +1 -1
  159. package/dist/esm/transformations/blog-post.js +4 -1
  160. package/dist/esm/transformations/blog-post.js.map +1 -1
  161. package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
  162. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
  163. package/dist/esm/transformations/ecommerce-product.js +29 -2
  164. package/dist/esm/transformations/ecommerce-product.js.map +1 -1
  165. package/dist/esm/transformations/heading-anchors.d.ts +25 -0
  166. package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
  167. package/dist/esm/transformations/heading-anchors.js +25 -0
  168. package/dist/esm/transformations/heading-anchors.js.map +1 -0
  169. package/dist/esm/transformations/index.d.ts +47 -4
  170. package/dist/esm/transformations/index.d.ts.map +1 -1
  171. package/dist/esm/transformations/index.js +56 -18
  172. package/dist/esm/transformations/index.js.map +1 -1
  173. package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
  174. package/dist/esm/transformations/shared-utils.js +1 -1
  175. package/dist/esm/transformations/shared-utils.js.map +1 -1
  176. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  177. package/dist/esm/utils.d.ts.map +1 -1
  178. package/dist/esm/utils.js +1 -1
  179. package/dist/esm/utils.js.map +1 -1
  180. package/package.json +5 -5
  181. package/src/array-overlap-sql.ts +41 -0
  182. package/src/data-source-fetchers.ts +41 -5
  183. package/src/fetchers/javascript.ts +9 -1
  184. package/src/fetchers/postgresql.ts +37 -10
  185. package/src/fetchers/raw-query.ts +60 -5
  186. package/src/fetchers/rest-api.ts +27 -5
  187. package/src/fetchers/teleport.ts +37 -13
  188. package/src/fetchers/utils/browser-row-policy.ts +212 -0
  189. package/src/fetchers/utils/request-sql-guards.ts +25 -0
  190. package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
  191. package/src/fetchers/utils/table-access-guard.ts +39 -0
  192. package/src/items-path.ts +74 -0
  193. package/src/localized-columns.ts +13 -0
  194. package/src/pagination-plugin.ts +29 -7
  195. package/src/transformations/blog-adjacent-posts.ts +74 -0
  196. package/src/transformations/blog-comments.ts +132 -0
  197. package/src/transformations/blog-post.ts +38 -5
  198. package/src/transformations/ecommerce-product.ts +568 -5
  199. package/src/transformations/heading-anchors.ts +85 -0
  200. package/src/transformations/index.ts +165 -22
  201. package/src/transformations/shared-utils.ts +6 -0
  202. package/src/utils.ts +26 -4
@@ -0,0 +1,206 @@
1
+ import { readFileSync } from 'fs'
2
+ import { join } from 'path'
3
+ import { ProductDiscounts, ProductOptions } from '@teleporthq/teleport-shared'
4
+ import { getTransformWrapperCode } from '../src/transformations'
5
+ import { generateEcommerceProductTransformationCode } from '../src/transformations/ecommerce-product'
6
+ import { generateSharedTransformationCode } from '../src/transformations/shared-utils'
7
+
8
+ /**
9
+ * Product options in the GENERATED storefront transform. The fixture is a
10
+ * byte-for-byte copy of the GUI's
11
+ * `features/e-commerce/utils/product-options/__tests__/fixtures/product-options-storefront.fixture.json`,
12
+ * which the canvas transform (`packages/renderer/src/utils/ecommerce-products.ts`)
13
+ * runs in `ecommerce-products-options.test.ts`: both transforms must emit
14
+ * exactly these fields, or the configurator, the configured price and the
15
+ * card's quick-add gate differ between the editor and the published store.
16
+ */
17
+
18
+ type Build = (record: unknown, options?: Record<string, unknown>) => Record<string, unknown>
19
+
20
+ interface FixtureCase {
21
+ name: string
22
+ includeDetail: boolean
23
+ taxRate: number
24
+ currentLanguage: string | null
25
+ mainLanguage: string | null
26
+ variants: Array<Record<string, unknown>> | null
27
+ record: Record<string, unknown>
28
+ expected: Record<string, unknown>
29
+ }
30
+
31
+ const readJson = <T>(file: string): T => JSON.parse(readFileSync(join(__dirname, file), 'utf8'))
32
+
33
+ const FIXTURE = readJson<{ cases: FixtureCase[] }>('product-options-storefront.fixture.json')
34
+ const PRE_OPTIONS_GOLDEN = readJson<{
35
+ records: Array<Record<string, unknown>>
36
+ variantsByProductId: Record<string, unknown>
37
+ assetMap: Record<string, unknown>
38
+ storefrontTaxRate: number
39
+ expected: Array<Record<string, unknown>>
40
+ }>('ecommerce-product-pre-options.golden.json')
41
+
42
+ const transformCode = (storefrontTaxRate: number): string =>
43
+ generateSharedTransformationCode() +
44
+ '\n' +
45
+ generateEcommerceProductTransformationCode({ storefrontTaxRate })
46
+
47
+ const buildProduct = (storefrontTaxRate: number): Build =>
48
+ new Function(transformCode(storefrontTaxRate) + '\nreturn buildEcommerceProduct;')() as Build
49
+
50
+ const OPTION_FIELDS = Object.keys(FIXTURE.cases[0].expected)
51
+
52
+ const pickOptionFields = (product: Record<string, unknown>) =>
53
+ OPTION_FIELDS.reduce<Record<string, unknown>>((picked, field) => {
54
+ picked[field] = product[field]
55
+ return picked
56
+ }, {})
57
+
58
+ const withoutOptionFields = (product: Record<string, unknown>) =>
59
+ Object.keys(product)
60
+ .filter((field) => OPTION_FIELDS.indexOf(field) === -1)
61
+ .reduce<Record<string, unknown>>((kept, field) => {
62
+ kept[field] = product[field]
63
+ return kept
64
+ }, {})
65
+
66
+ const EMPTY_OPTION_FIELDS = FIXTURE.cases.find(
67
+ (entry) => entry.name === 'a product without options'
68
+ )?.expected as Record<string, unknown>
69
+
70
+ const PRINT_GROUPS = [
71
+ {
72
+ key: 'paper',
73
+ label: 'Paper',
74
+ type: 'choice',
75
+ display: 'cards',
76
+ required: true,
77
+ values: [
78
+ {
79
+ key: 'matte',
80
+ label: 'Matte',
81
+ price: { mode: 'none' },
82
+ default: true,
83
+ imageUrl: 'TQ_matte',
84
+ },
85
+ ],
86
+ },
87
+ ]
88
+
89
+ describe('product transform — product options fixture (shared with the canvas)', () => {
90
+ it.each(FIXTURE.cases.map((entry) => [entry.name, entry] as [string, FixtureCase]))(
91
+ '%s',
92
+ (_name, entry) => {
93
+ const product = buildProduct(entry.taxRate)(entry.record, {
94
+ assetMap: {},
95
+ currentLanguage: entry.currentLanguage,
96
+ mainLanguage: entry.mainLanguage,
97
+ variantsByProductId: entry.variants ? { [String(entry.record.id)]: entry.variants } : null,
98
+ includeOptionDetail: entry.includeDetail,
99
+ })
100
+ expect(pickOptionFields(product)).toEqual(entry.expected)
101
+ }
102
+ )
103
+ })
104
+
105
+ describe('product transform — products without options are unchanged', () => {
106
+ it.each([false, true])(
107
+ 'equals the pre-options output plus empty fields (detail: %s)',
108
+ (detail) => {
109
+ const build = buildProduct(PRE_OPTIONS_GOLDEN.storefrontTaxRate)
110
+ PRE_OPTIONS_GOLDEN.records.forEach((record, index) => {
111
+ const product = build(record, {
112
+ assetMap: PRE_OPTIONS_GOLDEN.assetMap,
113
+ currentLanguage: null,
114
+ mainLanguage: null,
115
+ variantsByProductId: PRE_OPTIONS_GOLDEN.variantsByProductId,
116
+ ratingsByProductId: {},
117
+ reviewsByProductId: {},
118
+ includeOptionDetail: detail,
119
+ })
120
+ expect(JSON.parse(JSON.stringify(withoutOptionFields(product)))).toEqual(
121
+ PRE_OPTIONS_GOLDEN.expected[index]
122
+ )
123
+ expect(pickOptionFields(product)).toEqual(EMPTY_OPTION_FIELDS)
124
+ })
125
+ }
126
+ )
127
+ })
128
+
129
+ describe('product transform — option detail wiring', () => {
130
+ const record = { id: 'p1', name: 'Print', price: 10, option_groups: JSON.stringify(PRINT_GROUPS) }
131
+
132
+ it('resolves a value image stored as an asset id through the asset map', () => {
133
+ const product = buildProduct(0)(record, {
134
+ assetMap: { TQ_matte: { remoteSrc: 'https://cdn.example.com/matte.jpg' } },
135
+ includeOptionDetail: true,
136
+ })
137
+ const groups = product.optionGroups as Array<{ values: Array<Record<string, unknown>> }>
138
+ expect(groups[0].values[0]).toMatchObject({
139
+ imageUrl: 'https://cdn.example.com/matte.jpg',
140
+ showImage: 'true',
141
+ })
142
+ })
143
+
144
+ it('never hands a related product the option detail', () => {
145
+ const parent = buildProduct(0)(
146
+ { id: 'p0', name: 'Frame', price: 5, related_product_ids: '["p1"]' },
147
+ { includeOptionDetail: true, relatedProductsById: { p1: record } }
148
+ )
149
+ const [related] = parent.relatedProducts as Array<Record<string, unknown>>
150
+ expect(related.hasProductOptions).toBe('true')
151
+ expect(related.optionGroups).toEqual([])
152
+ expect(related.optionsPricingJson).toBe('{}')
153
+ })
154
+
155
+ it('splices the options helpers right after the discount helpers', () => {
156
+ const code = generateEcommerceProductTransformationCode({})
157
+ const discounts = code.indexOf(ProductDiscounts.generateProductDiscountHelperCode())
158
+ const options = code.indexOf(ProductOptions.generateProductOptionsHelperCode())
159
+ expect(discounts).toBeGreaterThan(-1)
160
+ expect(options).toBe(
161
+ discounts + ProductDiscounts.generateProductDiscountHelperCode().length + 1
162
+ )
163
+ })
164
+ })
165
+
166
+ describe('transform wrapper — option detail on the single-record fetch only', () => {
167
+ // Every lookup fails (no database): the maps fall back to their "unknown" or
168
+ // empty answers, which is all the transform needs here.
169
+ const offline = () => ({
170
+ connect: () => Promise.reject(new Error('offline')),
171
+ query: () => Promise.reject(new Error('offline')),
172
+ end: () => Promise.resolve(),
173
+ })
174
+ const transformRecords = new Function(
175
+ transformCode(0) +
176
+ '\n' +
177
+ getTransformWrapperCode('teleport_products') +
178
+ '\nreturn transformRecords;'
179
+ )() as (
180
+ records: unknown[],
181
+ getClient: () => unknown,
182
+ query: Record<string, unknown>
183
+ ) => Promise<Array<Record<string, unknown>>>
184
+
185
+ const product = (id: string) => ({
186
+ id,
187
+ name: id,
188
+ price: 10,
189
+ option_groups: JSON.stringify(PRINT_GROUPS),
190
+ })
191
+
192
+ it('emits the detail for the details page', async () => {
193
+ const [details] = await transformRecords([product('p1')], offline, {})
194
+ expect((details.optionGroups as unknown[]).length).toBe(1)
195
+ expect(details.optionsFreshKey).toBe('fresh:p1')
196
+ })
197
+
198
+ it('keeps a listing to the flags', async () => {
199
+ const listing = await transformRecords([product('p1'), product('p2')], offline, {})
200
+ for (const card of listing) {
201
+ expect(card.hasProductOptions).toBe('true')
202
+ expect(card.optionGroups).toEqual([])
203
+ expect(card.optionGroupsJson).toBe('[]')
204
+ }
205
+ })
206
+ })
@@ -0,0 +1,82 @@
1
+ import { generateEcommerceProductTransformationCode } from '../src/transformations/ecommerce-product'
2
+ import { generateSharedTransformationCode } from '../src/transformations/shared-utils'
3
+ import { buildProductTransformOptions } from '../src/transformations'
4
+ import type { UIDLEcommerceProductPages } from '@teleporthq/teleport-types'
5
+ import fixtures from './product-page-url-fixtures.json'
6
+
7
+ /**
8
+ * Custom product pages: with `ecommerceSettings.productPages` baked in, every
9
+ * product carries `productPageKey` (the page it opens on, '' = the standard
10
+ * one) and `productPageUrl` (its canonical address) — the product pages'
11
+ * guarded redirect and every product link read them. Without it the transform
12
+ * is exactly what it was. The addresses are pinned against the GUI's own
13
+ * builder by the SAME fixture file (`product-page-url-fixtures.json`, a copy of
14
+ * the GUI's `utils/product-pages/__tests__/fixtures/` one).
15
+ */
16
+
17
+ type BuildProduct = (record: unknown, options?: unknown) => Record<string, unknown>
18
+
19
+ const evalBuildProduct = (productPages?: UIDLEcommerceProductPages): BuildProduct => {
20
+ const code =
21
+ generateSharedTransformationCode() +
22
+ '\n' +
23
+ generateEcommerceProductTransformationCode({ productPages })
24
+ return new Function(code + '\nreturn buildEcommerceProduct;')() as BuildProduct
25
+ }
26
+
27
+ const PRODUCT = { id: '7', name: 'Mug', slug: 'mug', price: 12, currency: 'USD' }
28
+
29
+ describe('ecommerce product transform — product pages', () => {
30
+ it('adds nothing to a store without custom product pages', () => {
31
+ const code = generateEcommerceProductTransformationCode({})
32
+ expect(code).not.toContain('PRODUCT_PAGES')
33
+ expect(code).not.toContain('productPageUrl')
34
+ const product = evalBuildProduct()({ ...PRODUCT, product_page: 'personalise' })
35
+ expect(product).not.toHaveProperty('productPageKey')
36
+ expect(product).not.toHaveProperty('productPageUrl')
37
+ })
38
+
39
+ it("builds every product's page and address exactly as the GUI does", () => {
40
+ const build = evalBuildProduct(fixtures.config as UIDLEcommerceProductPages)
41
+ for (const fixture of fixtures.cases) {
42
+ const product = build({
43
+ ...PRODUCT,
44
+ ...fixture.values,
45
+ product_page: fixture.raw,
46
+ })
47
+ expect({
48
+ name: fixture.name,
49
+ key: product.productPageKey,
50
+ url: product.productPageUrl,
51
+ }).toEqual({
52
+ name: fixture.name,
53
+ key: fixture.key,
54
+ url: fixture.url,
55
+ })
56
+ }
57
+ })
58
+
59
+ it('reads the camel-cased column a non-SQL source hands back', () => {
60
+ const build = evalBuildProduct(fixtures.config as UIDLEcommerceProductPages)
61
+ const product = build({ ...PRODUCT, slug: 'postcard', productPage: 'personalise' })
62
+ expect(product.productPageUrl).toBe('/personalise/postcard')
63
+ })
64
+
65
+ it('bakes routes only — a page name never reaches the site', () => {
66
+ const code = generateEcommerceProductTransformationCode({
67
+ productPages: fixtures.config as UIDLEcommerceProductPages,
68
+ })
69
+ expect(code).not.toContain('Personalise')
70
+ // The helpers add no backtick: the transform is spliced into template literals.
71
+ const backticks = (text: string) => text.split('`').length
72
+ expect(backticks(code)).toBe(backticks(generateEcommerceProductTransformationCode({})))
73
+ })
74
+
75
+ it('takes the config from the exported e-commerce settings', () => {
76
+ const options = buildProductTransformOptions({
77
+ ecommerceSettings: { productPages: fixtures.config },
78
+ } as never)
79
+ expect(options.productPages).toEqual(fixtures.config)
80
+ expect(buildProductTransformOptions({} as never).productPages).toBeUndefined()
81
+ })
82
+ })