@teleporthq/teleport-plugin-next-data-source 0.43.66 → 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.
- package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
- package/__tests__/array-overlap-sql.test.ts +53 -0
- package/__tests__/blog-post-page-enrichment.test.ts +214 -0
- package/__tests__/browser-row-policy.test.ts +503 -0
- package/__tests__/data-source-cache-wrapping.test.ts +18 -2
- package/__tests__/data-source-request-locale.test.ts +313 -0
- package/__tests__/ecommerce-product-options.test.ts +206 -0
- package/__tests__/ecommerce-product-pages.test.ts +82 -0
- package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
- package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
- package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
- package/__tests__/fetchers.test.ts +2 -2
- package/__tests__/gated-data-provider.test.ts +88 -0
- package/__tests__/items-path.test.ts +150 -0
- package/__tests__/localized-columns.test.ts +217 -0
- package/__tests__/numbered-pagination.test.ts +15 -0
- package/__tests__/product-filter-fields.ts +230 -0
- package/__tests__/product-filter-sql.fixture.json +7 -0
- package/__tests__/product-options-storefront.fixture.json +2510 -0
- package/__tests__/product-page-url-fixtures.json +25 -0
- package/__tests__/product-price-sort.ts +4 -2
- package/__tests__/raw-rows-mode.test.ts +111 -0
- package/__tests__/refetch-loading-state.test.ts +23 -0
- package/__tests__/rest-api-response-cache.test.ts +434 -0
- package/__tests__/search-clear-button.test.ts +122 -0
- package/__tests__/table-read-route-protected-tables.test.ts +493 -0
- package/dist/cjs/array-overlap-sql.d.ts +21 -0
- package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
- package/dist/cjs/array-overlap-sql.js +25 -0
- package/dist/cjs/array-overlap-sql.js.map +1 -0
- package/dist/cjs/content-localization.d.ts +37 -0
- package/dist/cjs/content-localization.d.ts.map +1 -0
- package/dist/cjs/content-localization.js +29 -0
- package/dist/cjs/content-localization.js.map +1 -0
- package/dist/cjs/count-effect.d.ts +11 -1
- package/dist/cjs/count-effect.d.ts.map +1 -1
- package/dist/cjs/count-effect.js +30 -2
- package/dist/cjs/count-effect.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +10 -4
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +1 -1
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +1 -1
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +9 -4
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +8 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +42 -4
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +1 -1
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +7 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +16 -6
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +60 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/items-path.d.ts +10 -0
- package/dist/cjs/items-path.d.ts.map +1 -0
- package/dist/cjs/items-path.js +86 -0
- package/dist/cjs/items-path.js.map +1 -0
- package/dist/cjs/loading-state.d.ts +16 -1
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +69 -3
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/localized-columns.d.ts +56 -0
- package/dist/cjs/localized-columns.d.ts.map +1 -0
- package/dist/cjs/localized-columns.js +137 -0
- package/dist/cjs/localized-columns.js.map +1 -0
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +140 -50
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/product-filter-fields.d.ts +87 -0
- package/dist/cjs/product-filter-fields.d.ts.map +1 -0
- package/dist/cjs/product-filter-fields.js +161 -0
- package/dist/cjs/product-filter-fields.js.map +1 -0
- package/dist/cjs/request-locale.d.ts +49 -0
- package/dist/cjs/request-locale.d.ts.map +1 -0
- package/dist/cjs/request-locale.js +130 -0
- package/dist/cjs/request-locale.js.map +1 -0
- package/dist/cjs/search-clear.d.ts +31 -0
- package/dist/cjs/search-clear.d.ts.map +1 -0
- package/dist/cjs/search-clear.js +103 -0
- package/dist/cjs/search-clear.js.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/cjs/transformations/blog-comments.d.ts +30 -0
- package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-comments.js +34 -0
- package/dist/cjs/transformations/blog-comments.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +2 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +4 -1
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +28 -1
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
- package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/cjs/transformations/heading-anchors.js +29 -0
- package/dist/cjs/transformations/heading-anchors.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +61 -4
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +69 -19
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
- package/dist/cjs/transformations/shared-utils.js +1 -1
- package/dist/cjs/transformations/shared-utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +11 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +60 -16
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-overlap-sql.d.ts +21 -0
- package/dist/esm/array-overlap-sql.d.ts.map +1 -0
- package/dist/esm/array-overlap-sql.js +21 -0
- package/dist/esm/array-overlap-sql.js.map +1 -0
- package/dist/esm/content-localization.d.ts +37 -0
- package/dist/esm/content-localization.d.ts.map +1 -0
- package/dist/esm/content-localization.js +24 -0
- package/dist/esm/content-localization.js.map +1 -0
- package/dist/esm/count-effect.d.ts +11 -1
- package/dist/esm/count-effect.d.ts.map +1 -1
- package/dist/esm/count-effect.js +28 -1
- package/dist/esm/count-effect.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +11 -5
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +1 -1
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +1 -1
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
- package/dist/esm/fetchers/mongodb.js +1 -1
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +1 -1
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +9 -4
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +8 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +42 -4
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +1 -1
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +7 -2
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +16 -6
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +1 -1
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
- package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +55 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/items-path.d.ts +10 -0
- package/dist/esm/items-path.d.ts.map +1 -0
- package/dist/esm/items-path.js +58 -0
- package/dist/esm/items-path.js.map +1 -0
- package/dist/esm/loading-state.d.ts +16 -1
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +69 -3
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/localized-columns.d.ts +56 -0
- package/dist/esm/localized-columns.d.ts.map +1 -0
- package/dist/esm/localized-columns.js +132 -0
- package/dist/esm/localized-columns.js.map +1 -0
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +141 -51
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/product-filter-fields.d.ts +87 -0
- package/dist/esm/product-filter-fields.d.ts.map +1 -0
- package/dist/esm/product-filter-fields.js +154 -0
- package/dist/esm/product-filter-fields.js.map +1 -0
- package/dist/esm/request-locale.d.ts +49 -0
- package/dist/esm/request-locale.d.ts.map +1 -0
- package/dist/esm/request-locale.js +98 -0
- package/dist/esm/request-locale.js.map +1 -0
- package/dist/esm/search-clear.d.ts +31 -0
- package/dist/esm/search-clear.d.ts.map +1 -0
- package/dist/esm/search-clear.js +74 -0
- package/dist/esm/search-clear.js.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
- package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/esm/transformations/blog-comments.d.ts +30 -0
- package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
- package/dist/esm/transformations/blog-comments.js +30 -0
- package/dist/esm/transformations/blog-comments.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +2 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +4 -1
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +29 -2
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/heading-anchors.d.ts +25 -0
- package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/esm/transformations/heading-anchors.js +25 -0
- package/dist/esm/transformations/heading-anchors.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +61 -4
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +69 -19
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
- package/dist/esm/transformations/shared-utils.js +1 -1
- package/dist/esm/transformations/shared-utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +11 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +60 -16
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/array-overlap-sql.ts +41 -0
- package/src/content-localization.ts +53 -0
- package/src/count-effect.ts +53 -2
- package/src/data-source-fetchers.ts +41 -5
- package/src/fetchers/airtable.ts +4 -2
- package/src/fetchers/clickhouse.ts +4 -2
- package/src/fetchers/firestore.ts +4 -2
- package/src/fetchers/javascript.ts +9 -1
- package/src/fetchers/mariadb.ts +4 -2
- package/src/fetchers/mongodb.ts +4 -2
- package/src/fetchers/mysql.ts +4 -2
- package/src/fetchers/postgresql.ts +53 -12
- package/src/fetchers/raw-query.ts +60 -5
- package/src/fetchers/redshift.ts +4 -2
- package/src/fetchers/rest-api.ts +27 -5
- package/src/fetchers/teleport.ts +85 -22
- package/src/fetchers/turso.ts +4 -2
- package/src/fetchers/utils/browser-row-policy.ts +212 -0
- package/src/fetchers/utils/request-sql-guards.ts +25 -0
- package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
- package/src/fetchers/utils/table-access-guard.ts +39 -0
- package/src/index.ts +79 -11
- package/src/items-path.ts +74 -0
- package/src/loading-state.ts +134 -3
- package/src/localized-columns.ts +146 -0
- package/src/pagination-plugin.ts +206 -60
- package/src/product-filter-fields.ts +173 -0
- package/src/request-locale.ts +113 -0
- package/src/search-clear.ts +102 -0
- package/src/transformations/blog-adjacent-posts.ts +74 -0
- package/src/transformations/blog-comments.ts +132 -0
- package/src/transformations/blog-post.ts +38 -5
- package/src/transformations/ecommerce-product.ts +568 -5
- package/src/transformations/heading-anchors.ts +85 -0
- package/src/transformations/index.ts +191 -23
- package/src/transformations/shared-utils.ts +6 -0
- package/src/utils.ts +109 -19
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import * as types from '@babel/types'
|
|
2
|
+
import generator from '@babel/generator'
|
|
3
|
+
import {
|
|
4
|
+
ChunkType,
|
|
5
|
+
FileType,
|
|
6
|
+
ComponentStructure,
|
|
7
|
+
ChunkDefinition,
|
|
8
|
+
GeneratorOptions,
|
|
9
|
+
UIDLDataSource,
|
|
10
|
+
UIDLDependency,
|
|
11
|
+
} from '@teleporthq/teleport-types'
|
|
12
|
+
import { createNextPagesDataSourcePlugin, createNextComponentDataSourcePlugin } from '../src/index'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A page in a multi-language project renders rows in ITS language: every
|
|
16
|
+
* data-source fetch — the build-time prefetch in getStaticProps and the
|
|
17
|
+
* client-side refetch behind pagination, search and filters — says which
|
|
18
|
+
* locale it is for, and a locale switch (a client-side transition on the same
|
|
19
|
+
* page instance) refetches. A single-language project generates exactly what
|
|
20
|
+
* it generated before: no locale, no router.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const DS_ID = 'ds1'
|
|
24
|
+
const TABLE = 'teleport_products'
|
|
25
|
+
/** A page writes the fetcher to `utils/`, a component straight into `api/`. */
|
|
26
|
+
const FETCHER_KEYS = ['utils/teleport-teleport_products-ds1', 'api/teleport-teleport_products-ds1']
|
|
27
|
+
|
|
28
|
+
const I18N: GeneratorOptions['internationalization'] = {
|
|
29
|
+
main: { name: 'English', locale: 'en' },
|
|
30
|
+
languages: { en: 'English', es: 'Spanish' },
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const dataSource: UIDLDataSource = {
|
|
34
|
+
id: DS_ID,
|
|
35
|
+
name: 'TeleportHQ database',
|
|
36
|
+
type: 'teleport',
|
|
37
|
+
config: {
|
|
38
|
+
selectedTables: {
|
|
39
|
+
[TABLE]: {
|
|
40
|
+
columns: [{ name: 'id' }, { name: 'name' }, { name: 'slug' }, { name: 'es_name' }],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const jsx = (name: string, attrs: types.JSXAttribute[], children: types.JSXElement[] = []) =>
|
|
47
|
+
types.jsxElement(
|
|
48
|
+
types.jsxOpeningElement(types.jsxIdentifier(name), attrs, children.length === 0),
|
|
49
|
+
children.length === 0 ? null : types.jsxClosingElement(types.jsxIdentifier(name)),
|
|
50
|
+
children,
|
|
51
|
+
children.length === 0
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const resourceDefinitionAttr = () =>
|
|
55
|
+
types.jsxAttribute(
|
|
56
|
+
types.jsxIdentifier('resourceDefinition'),
|
|
57
|
+
types.jsxExpressionContainer(
|
|
58
|
+
types.objectExpression([
|
|
59
|
+
types.objectProperty(types.stringLiteral('dataSourceId'), types.stringLiteral(DS_ID)),
|
|
60
|
+
types.objectProperty(types.stringLiteral('tableName'), types.stringLiteral(TABLE)),
|
|
61
|
+
types.objectProperty(
|
|
62
|
+
types.stringLiteral('dataSourceType'),
|
|
63
|
+
types.stringLiteral('teleport')
|
|
64
|
+
),
|
|
65
|
+
])
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
/** `<DataProvider name={'items'} resourceDefinition={…} renderSuccess={(items) => <Repeater …/>} />` */
|
|
70
|
+
const makeListProvider = (): types.JSXElement => {
|
|
71
|
+
const repeater = jsx('Repeater', [
|
|
72
|
+
types.jsxAttribute(
|
|
73
|
+
types.jsxIdentifier('items'),
|
|
74
|
+
types.jsxExpressionContainer(types.identifier('items'))
|
|
75
|
+
),
|
|
76
|
+
types.jsxAttribute(
|
|
77
|
+
types.jsxIdentifier('renderItem'),
|
|
78
|
+
types.jsxExpressionContainer(
|
|
79
|
+
types.arrowFunctionExpression([types.identifier('product')], jsx('div', []))
|
|
80
|
+
)
|
|
81
|
+
),
|
|
82
|
+
])
|
|
83
|
+
return jsx('DataProvider', [
|
|
84
|
+
types.jsxAttribute(
|
|
85
|
+
types.jsxIdentifier('name'),
|
|
86
|
+
types.jsxExpressionContainer(types.stringLiteral('items'))
|
|
87
|
+
),
|
|
88
|
+
resourceDefinitionAttr(),
|
|
89
|
+
types.jsxAttribute(
|
|
90
|
+
types.jsxIdentifier('renderSuccess'),
|
|
91
|
+
types.jsxExpressionContainer(
|
|
92
|
+
types.arrowFunctionExpression([types.identifier('items')], repeater)
|
|
93
|
+
)
|
|
94
|
+
),
|
|
95
|
+
])
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** `<DataProvider name={'item'} resourceDefinition={…} renderSuccess={(item) => <div/>} />` */
|
|
99
|
+
const makeItemProvider = (): types.JSXElement =>
|
|
100
|
+
jsx('DataProvider', [
|
|
101
|
+
types.jsxAttribute(
|
|
102
|
+
types.jsxIdentifier('name'),
|
|
103
|
+
types.jsxExpressionContainer(types.stringLiteral('item'))
|
|
104
|
+
),
|
|
105
|
+
resourceDefinitionAttr(),
|
|
106
|
+
types.jsxAttribute(
|
|
107
|
+
types.jsxIdentifier('renderSuccess'),
|
|
108
|
+
types.jsxExpressionContainer(
|
|
109
|
+
types.arrowFunctionExpression([types.identifier('item')], jsx('div', []))
|
|
110
|
+
)
|
|
111
|
+
),
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
const makeComponentChunk = (provider: types.JSXElement): ChunkDefinition => ({
|
|
115
|
+
name: 'jsx-component',
|
|
116
|
+
type: ChunkType.AST,
|
|
117
|
+
fileType: FileType.JS,
|
|
118
|
+
linkAfter: [],
|
|
119
|
+
content: types.variableDeclaration('const', [
|
|
120
|
+
types.variableDeclarator(
|
|
121
|
+
types.identifier('ProductsList'),
|
|
122
|
+
types.arrowFunctionExpression(
|
|
123
|
+
[types.identifier('props')],
|
|
124
|
+
types.blockStatement([types.returnStatement(jsx('div', [], [provider]))])
|
|
125
|
+
)
|
|
126
|
+
),
|
|
127
|
+
]),
|
|
128
|
+
meta: { nodesLookup: { provider } },
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// tslint:disable-next-line:no-any
|
|
132
|
+
const makeListNode = (paginated: boolean): any => ({
|
|
133
|
+
type: 'data-source-list',
|
|
134
|
+
content: {
|
|
135
|
+
renderPropIdentifier: 'items',
|
|
136
|
+
resourceDefinition: { dataSourceId: DS_ID, tableName: TABLE, dataSourceType: 'teleport' },
|
|
137
|
+
resource: {
|
|
138
|
+
id: 'TQ_products',
|
|
139
|
+
params: { queryColumns: { type: 'static', content: ['name', 'es_name'] } },
|
|
140
|
+
},
|
|
141
|
+
nodes: {
|
|
142
|
+
success: {
|
|
143
|
+
type: 'cms-list-repeater',
|
|
144
|
+
content: {
|
|
145
|
+
renderPropIdentifier: 'product',
|
|
146
|
+
paginated,
|
|
147
|
+
perPage: 20,
|
|
148
|
+
searchEnabled: false,
|
|
149
|
+
nodes: { list: { type: 'element', content: { elementType: 'div' } } },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
/** An item whose params cannot be resolved at build time, so it fetches client-side. */
|
|
157
|
+
// tslint:disable-next-line:no-any
|
|
158
|
+
const makeDynamicItemNode = (withParams: boolean): any => ({
|
|
159
|
+
type: 'data-source-item',
|
|
160
|
+
content: {
|
|
161
|
+
renderPropIdentifier: 'item',
|
|
162
|
+
resourceDefinition: { dataSourceId: DS_ID, tableName: TABLE, dataSourceType: 'teleport' },
|
|
163
|
+
resource: {
|
|
164
|
+
params: withParams
|
|
165
|
+
? { limit: { type: 'dynamic', content: { referenceType: 'prop', id: 'limit' } } }
|
|
166
|
+
: {},
|
|
167
|
+
},
|
|
168
|
+
children: [{ type: 'element', content: { elementType: 'div' } }],
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const run = async (
|
|
173
|
+
plugin: ReturnType<typeof createNextPagesDataSourcePlugin>,
|
|
174
|
+
// tslint:disable-next-line:no-any
|
|
175
|
+
node: any,
|
|
176
|
+
provider: types.JSXElement,
|
|
177
|
+
localized: boolean
|
|
178
|
+
) => {
|
|
179
|
+
const chunk = makeComponentChunk(provider)
|
|
180
|
+
const dependencies: Record<string, UIDLDependency> = {}
|
|
181
|
+
// tslint:disable-next-line:no-any
|
|
182
|
+
const extractedResources: Record<string, any> = {}
|
|
183
|
+
const structure: ComponentStructure = {
|
|
184
|
+
uidl: { name: 'ProductsList', node, outputOptions: { folderPath: [] } },
|
|
185
|
+
chunks: [chunk],
|
|
186
|
+
dependencies,
|
|
187
|
+
options: {
|
|
188
|
+
dataSources: { [DS_ID]: dataSource },
|
|
189
|
+
extractedResources,
|
|
190
|
+
...(localized ? { internationalization: I18N } : {}),
|
|
191
|
+
},
|
|
192
|
+
} as never
|
|
193
|
+
await plugin(structure)
|
|
194
|
+
const getStaticProps = structure.chunks.find((c) => c.name === 'getStaticProps')
|
|
195
|
+
return {
|
|
196
|
+
code: generator(chunk.content as types.Node).code,
|
|
197
|
+
staticProps: getStaticProps ? generator(getStaticProps.content as types.Node).code : '',
|
|
198
|
+
dependencies,
|
|
199
|
+
fetcher: FETCHER_KEYS.map((key) => extractedResources[key]?.content).find(Boolean) as string,
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const collapse = (code: string): string => code.replace(/\s+/g, ' ')
|
|
204
|
+
|
|
205
|
+
describe('data-source fetches in a multi-language project', () => {
|
|
206
|
+
const pagesPlugin = createNextPagesDataSourcePlugin()
|
|
207
|
+
const componentPlugin = createNextComponentDataSourcePlugin()
|
|
208
|
+
|
|
209
|
+
describe('a paginated list page', () => {
|
|
210
|
+
it('fetches the first page for the language getStaticProps runs for', async () => {
|
|
211
|
+
const { staticProps } = await run(pagesPlugin, makeListNode(true), makeListProvider(), true)
|
|
212
|
+
expect(collapse(staticProps)).toContain(
|
|
213
|
+
'fetchData({ page: 1, perPage: 20, queryColumns: JSON.stringify(["name", "es_name"]), locale: context.locale })'
|
|
214
|
+
)
|
|
215
|
+
// The count is the same in every language.
|
|
216
|
+
expect(collapse(staticProps)).toContain('fetchCount().catch')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('sends the client locale with every refetch and refetches when it changes', async () => {
|
|
220
|
+
const { code, dependencies } = await run(
|
|
221
|
+
pagesPlugin,
|
|
222
|
+
makeListNode(true),
|
|
223
|
+
makeListProvider(),
|
|
224
|
+
true
|
|
225
|
+
)
|
|
226
|
+
const flat = collapse(code)
|
|
227
|
+
expect(flat).toContain(
|
|
228
|
+
'params={useMemo(() => ({ page: ds_0_page, perPage: 20, locale: router?.locale }), [ds_0_page, router?.locale])}'
|
|
229
|
+
)
|
|
230
|
+
expect(code).toContain('const router = useRouter()')
|
|
231
|
+
expect(dependencies.useRouter).toMatchObject({ type: 'library', path: 'next/router' })
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('bakes the per-language columns into the fetcher it emits', async () => {
|
|
235
|
+
const { fetcher } = await run(pagesPlugin, makeListNode(true), makeListProvider(), true)
|
|
236
|
+
expect(fetcher).toContain('var LOCALIZED_COLUMNS = {"name":{"es":"es_name"}}')
|
|
237
|
+
expect(fetcher).toContain('mainLanguage: "en"')
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
describe('a plain list page', () => {
|
|
242
|
+
it('prefetches and refetches in the page language', async () => {
|
|
243
|
+
const { code, staticProps } = await run(
|
|
244
|
+
pagesPlugin,
|
|
245
|
+
makeListNode(false),
|
|
246
|
+
makeListProvider(),
|
|
247
|
+
true
|
|
248
|
+
)
|
|
249
|
+
expect(collapse(staticProps)).toContain(
|
|
250
|
+
'fetchData({ "queryColumns": JSON.stringify(["name", "es_name"]), locale: context.locale })'
|
|
251
|
+
)
|
|
252
|
+
expect(collapse(code)).toContain(
|
|
253
|
+
'params={useMemo(() => ({ locale: router?.locale }), [router?.locale])}'
|
|
254
|
+
)
|
|
255
|
+
expect(code).toContain('const router = useRouter()')
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('a component item that fetches client-side', () => {
|
|
260
|
+
it('adds the locale to the params it already sends', async () => {
|
|
261
|
+
const { code, dependencies } = await run(
|
|
262
|
+
componentPlugin,
|
|
263
|
+
makeDynamicItemNode(true),
|
|
264
|
+
makeItemProvider(),
|
|
265
|
+
true
|
|
266
|
+
)
|
|
267
|
+
const flat = collapse(code)
|
|
268
|
+
expect(flat).toContain('locale: router?.locale')
|
|
269
|
+
// The route is a template literal that interpolates the params.
|
|
270
|
+
expect(flat).toContain(
|
|
271
|
+
'fetch(`/api/teleport-teleport_products-ds1?$' + '{new URLSearchParams(params)}`'
|
|
272
|
+
)
|
|
273
|
+
expect(code).toContain('const router = useRouter()')
|
|
274
|
+
expect(dependencies.useRouter).toBeDefined()
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('creates the params when the node declared none', async () => {
|
|
278
|
+
const { code } = await run(
|
|
279
|
+
componentPlugin,
|
|
280
|
+
makeDynamicItemNode(false),
|
|
281
|
+
makeItemProvider(),
|
|
282
|
+
true
|
|
283
|
+
)
|
|
284
|
+
const flat = collapse(code)
|
|
285
|
+
expect(flat).toContain('params={{ locale: router?.locale }}')
|
|
286
|
+
expect(flat).toContain('new URLSearchParams(params)')
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
describe('a single-language project', () => {
|
|
291
|
+
it.each([
|
|
292
|
+
['a paginated list page', pagesPlugin, makeListNode(true), makeListProvider],
|
|
293
|
+
['a plain list page', pagesPlugin, makeListNode(false), makeListProvider],
|
|
294
|
+
['a component item', componentPlugin, makeDynamicItemNode(true), makeItemProvider],
|
|
295
|
+
])(
|
|
296
|
+
'generates %s exactly as before — no locale, no router',
|
|
297
|
+
async (_, plugin, node, provider) => {
|
|
298
|
+
const { code, staticProps, dependencies, fetcher } = await run(
|
|
299
|
+
plugin,
|
|
300
|
+
node,
|
|
301
|
+
provider(),
|
|
302
|
+
false
|
|
303
|
+
)
|
|
304
|
+
expect(code).not.toContain('locale')
|
|
305
|
+
expect(code).not.toContain('useRouter')
|
|
306
|
+
expect(staticProps).not.toContain('locale')
|
|
307
|
+
expect(dependencies.useRouter).toBeUndefined()
|
|
308
|
+
expect(fetcher).toContain('var LOCALIZED_COLUMNS = {}')
|
|
309
|
+
expect(fetcher).toContain('mainLanguage: null')
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
})
|
|
313
|
+
})
|
|
@@ -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
|
+
})
|