@teleporthq/teleport-project-generator-next 0.43.74 → 0.43.75
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__/analytics-project-plugin.test.ts +35 -2
- package/__tests__/analytics-tracked-pages.test.ts +90 -0
- package/__tests__/content-category-page-end2end.test.ts +205 -0
- package/__tests__/content-category-pages.test.ts +366 -0
- package/__tests__/content-site-index.test.ts +607 -0
- package/__tests__/rich-text-editor-embed-format.test.ts +75 -3
- package/dist/cjs/analytics/project-plugin.js +3 -3
- package/dist/cjs/analytics/project-plugin.js.map +1 -1
- package/dist/cjs/analytics/tracker-component.d.ts +3 -1
- package/dist/cjs/analytics/tracker-component.d.ts.map +1 -1
- package/dist/cjs/analytics/tracker-component.js +12 -3
- package/dist/cjs/analytics/tracker-component.js.map +1 -1
- package/dist/cjs/analytics/tracker-source.d.ts +1 -1
- package/dist/cjs/analytics/tracker-source.d.ts.map +1 -1
- package/dist/cjs/analytics/tracker-source.js +1 -1
- package/dist/cjs/analytics/tracker-source.js.map +1 -1
- package/dist/cjs/blog/arranged-order-runtime.d.ts +9 -0
- package/dist/cjs/blog/arranged-order-runtime.d.ts.map +1 -0
- package/dist/cjs/blog/arranged-order-runtime.js +12 -0
- package/dist/cjs/blog/arranged-order-runtime.js.map +1 -0
- package/dist/cjs/blog/blog-context-generator.d.ts +1 -1
- package/dist/cjs/blog/blog-context-generator.d.ts.map +1 -1
- package/dist/cjs/blog/blog-context-generator.js +3 -2
- package/dist/cjs/blog/blog-context-generator.js.map +1 -1
- package/dist/cjs/blog/content-category-pages.d.ts +40 -0
- package/dist/cjs/blog/content-category-pages.d.ts.map +1 -0
- package/dist/cjs/blog/content-category-pages.js +80 -0
- package/dist/cjs/blog/content-category-pages.js.map +1 -0
- package/dist/cjs/blog/content-site-index.d.ts +49 -0
- package/dist/cjs/blog/content-site-index.d.ts.map +1 -0
- package/dist/cjs/blog/content-site-index.js +213 -0
- package/dist/cjs/blog/content-site-index.js.map +1 -0
- package/dist/cjs/blog/project-plugin.d.ts +3 -1
- package/dist/cjs/blog/project-plugin.d.ts.map +1 -1
- package/dist/cjs/blog/project-plugin.js +69 -4
- package/dist/cjs/blog/project-plugin.js.map +1 -1
- package/dist/cjs/blog/rss-feed.d.ts.map +1 -1
- package/dist/cjs/blog/rss-feed.js +2 -1
- package/dist/cjs/blog/rss-feed.js.map +1 -1
- package/dist/cjs/internationalization/locale-mapper-component.d.ts +1 -0
- package/dist/cjs/internationalization/locale-mapper-component.d.ts.map +1 -1
- package/dist/cjs/internationalization/locale-mapper-component.js +20 -2
- package/dist/cjs/internationalization/locale-mapper-component.js.map +1 -1
- package/dist/cjs/rich-text-editor/component-generator.d.ts.map +1 -1
- package/dist/cjs/rich-text-editor/component-generator.js +10 -7
- package/dist/cjs/rich-text-editor/component-generator.js.map +1 -1
- package/dist/cjs/rich-text-editor/content-runtime-module.d.ts +9 -0
- package/dist/cjs/rich-text-editor/content-runtime-module.d.ts.map +1 -0
- package/dist/cjs/rich-text-editor/content-runtime-module.js +29 -0
- package/dist/cjs/rich-text-editor/content-runtime-module.js.map +1 -0
- package/dist/cjs/rich-text-editor/content-support-source.d.ts +10 -0
- package/dist/cjs/rich-text-editor/content-support-source.d.ts.map +1 -0
- package/dist/cjs/rich-text-editor/content-support-source.js +20 -0
- package/dist/cjs/rich-text-editor/content-support-source.js.map +1 -0
- package/dist/cjs/rich-text-editor/project-plugin.d.ts.map +1 -1
- package/dist/cjs/rich-text-editor/project-plugin.js +4 -1
- package/dist/cjs/rich-text-editor/project-plugin.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/analytics/project-plugin.js +4 -4
- package/dist/esm/analytics/project-plugin.js.map +1 -1
- package/dist/esm/analytics/tracker-component.d.ts +3 -1
- package/dist/esm/analytics/tracker-component.d.ts.map +1 -1
- package/dist/esm/analytics/tracker-component.js +10 -2
- package/dist/esm/analytics/tracker-component.js.map +1 -1
- package/dist/esm/analytics/tracker-source.d.ts +1 -1
- package/dist/esm/analytics/tracker-source.d.ts.map +1 -1
- package/dist/esm/analytics/tracker-source.js +1 -1
- package/dist/esm/analytics/tracker-source.js.map +1 -1
- package/dist/esm/blog/arranged-order-runtime.d.ts +9 -0
- package/dist/esm/blog/arranged-order-runtime.d.ts.map +1 -0
- package/dist/esm/blog/arranged-order-runtime.js +9 -0
- package/dist/esm/blog/arranged-order-runtime.js.map +1 -0
- package/dist/esm/blog/blog-context-generator.d.ts +1 -1
- package/dist/esm/blog/blog-context-generator.d.ts.map +1 -1
- package/dist/esm/blog/blog-context-generator.js +3 -2
- package/dist/esm/blog/blog-context-generator.js.map +1 -1
- package/dist/esm/blog/content-category-pages.d.ts +40 -0
- package/dist/esm/blog/content-category-pages.d.ts.map +1 -0
- package/dist/esm/blog/content-category-pages.js +75 -0
- package/dist/esm/blog/content-category-pages.js.map +1 -0
- package/dist/esm/blog/content-site-index.d.ts +49 -0
- package/dist/esm/blog/content-site-index.d.ts.map +1 -0
- package/dist/esm/blog/content-site-index.js +205 -0
- package/dist/esm/blog/content-site-index.js.map +1 -0
- package/dist/esm/blog/project-plugin.d.ts +3 -1
- package/dist/esm/blog/project-plugin.d.ts.map +1 -1
- package/dist/esm/blog/project-plugin.js +70 -5
- package/dist/esm/blog/project-plugin.js.map +1 -1
- package/dist/esm/blog/rss-feed.d.ts.map +1 -1
- package/dist/esm/blog/rss-feed.js +2 -1
- package/dist/esm/blog/rss-feed.js.map +1 -1
- package/dist/esm/internationalization/locale-mapper-component.d.ts +1 -0
- package/dist/esm/internationalization/locale-mapper-component.d.ts.map +1 -1
- package/dist/esm/internationalization/locale-mapper-component.js +19 -1
- package/dist/esm/internationalization/locale-mapper-component.js.map +1 -1
- package/dist/esm/rich-text-editor/component-generator.d.ts.map +1 -1
- package/dist/esm/rich-text-editor/component-generator.js +10 -7
- package/dist/esm/rich-text-editor/component-generator.js.map +1 -1
- package/dist/esm/rich-text-editor/content-runtime-module.d.ts +9 -0
- package/dist/esm/rich-text-editor/content-runtime-module.d.ts.map +1 -0
- package/dist/esm/rich-text-editor/content-runtime-module.js +25 -0
- package/dist/esm/rich-text-editor/content-runtime-module.js.map +1 -0
- package/dist/esm/rich-text-editor/content-support-source.d.ts +10 -0
- package/dist/esm/rich-text-editor/content-support-source.d.ts.map +1 -0
- package/dist/esm/rich-text-editor/content-support-source.js +17 -0
- package/dist/esm/rich-text-editor/content-support-source.js.map +1 -0
- package/dist/esm/rich-text-editor/project-plugin.d.ts.map +1 -1
- package/dist/esm/rich-text-editor/project-plugin.js +4 -1
- package/dist/esm/rich-text-editor/project-plugin.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -19
- package/src/analytics/project-plugin.ts +2 -2
- package/src/analytics/tracker-component.ts +12 -3
- package/src/analytics/tracker-source.ts +74 -3
- package/src/blog/arranged-order-runtime.ts +85 -0
- package/src/blog/blog-context-generator.ts +19 -1
- package/src/blog/content-category-pages.ts +212 -0
- package/src/blog/content-site-index.ts +945 -0
- package/src/blog/project-plugin.ts +81 -4
- package/src/blog/rss-feed.ts +2 -1
- package/src/internationalization/locale-mapper-component.ts +23 -0
- package/src/rich-text-editor/component-generator.ts +63 -17
- package/src/rich-text-editor/content-runtime-module.ts +26 -0
- package/src/rich-text-editor/content-support-source.ts +97 -0
- package/src/rich-text-editor/project-plugin.ts +4 -1
|
@@ -29,7 +29,11 @@ const GTM_ASSET = {
|
|
|
29
29
|
options: { target: 'body' },
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function makeStructure(
|
|
32
|
+
function makeStructure(
|
|
33
|
+
analyticsEnabled: boolean,
|
|
34
|
+
assets: unknown[] = [],
|
|
35
|
+
disabledPaths?: string[]
|
|
36
|
+
): ProjectPluginStructure {
|
|
33
37
|
const files = new Map()
|
|
34
38
|
files.set('_app', {
|
|
35
39
|
path: ['pages'],
|
|
@@ -41,7 +45,9 @@ function makeStructure(analyticsEnabled: boolean, assets: unknown[] = []): Proje
|
|
|
41
45
|
name: 'test-project',
|
|
42
46
|
globals: { settings: { title: 'Test', language: 'en' }, assets },
|
|
43
47
|
root: {} as never,
|
|
44
|
-
...(analyticsEnabled
|
|
48
|
+
...(analyticsEnabled
|
|
49
|
+
? { analytics: { enabled: true, ...(disabledPaths ? { disabledPaths } : {}) } }
|
|
50
|
+
: {}),
|
|
45
51
|
},
|
|
46
52
|
files,
|
|
47
53
|
dependencies: {},
|
|
@@ -186,6 +192,33 @@ describe('NextAnalyticsProjectPlugin', () => {
|
|
|
186
192
|
).not.toThrow()
|
|
187
193
|
})
|
|
188
194
|
|
|
195
|
+
it('bakes the pages kept out of analytics into the tracker component', async () => {
|
|
196
|
+
const plugin = new NextAnalyticsProjectPlugin()
|
|
197
|
+
const structure = makeStructure(true, [], ['/admin-panel/*', '/admin-panel', '/secret'])
|
|
198
|
+
|
|
199
|
+
await plugin.runAfter(structure)
|
|
200
|
+
|
|
201
|
+
const tracker = structure.files.get('teleport-analytics-tracker').files[0].content
|
|
202
|
+
expect(tracker).toContain('const EXCLUDED_PATHS = ["/admin-panel/*","/admin-panel","/secret"]')
|
|
203
|
+
expect(tracker).toContain(
|
|
204
|
+
'initTeleportAnalytics({ excludedPaths: EXCLUDED_PATHS, locales: router.locales || [] })'
|
|
205
|
+
)
|
|
206
|
+
expect(structure.files.get('teleport-analytics-lib').files[0].content).toContain(
|
|
207
|
+
'function isPathExcluded'
|
|
208
|
+
)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('tracks every page when the editor sent no list', async () => {
|
|
212
|
+
const plugin = new NextAnalyticsProjectPlugin()
|
|
213
|
+
const structure = makeStructure(true)
|
|
214
|
+
|
|
215
|
+
await plugin.runAfter(structure)
|
|
216
|
+
|
|
217
|
+
expect(structure.files.get('teleport-analytics-tracker').files[0].content).toContain(
|
|
218
|
+
'const EXCLUDED_PATHS = []'
|
|
219
|
+
)
|
|
220
|
+
})
|
|
221
|
+
|
|
189
222
|
it('preserves env values already set by the GUI mapper', async () => {
|
|
190
223
|
const plugin = new NextAnalyticsProjectPlugin()
|
|
191
224
|
const structure = makeStructure(true)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { TRACKER_SOURCE } from '../src/analytics/tracker-source'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The emitted tracker, executed: the module syntax is stripped the way a
|
|
5
|
+
* bundler resolves it, and the matching functions plus the two module-level
|
|
6
|
+
* lists are handed back so the tests drive the real code, not a copy of it.
|
|
7
|
+
*/
|
|
8
|
+
interface TrackerMatching {
|
|
9
|
+
setExcludedPaths: (paths: string[]) => void
|
|
10
|
+
setLocales: (locales: string[]) => void
|
|
11
|
+
isPathExcluded: (path: string) => boolean
|
|
12
|
+
normalizeTrackedPath: (path: string) => string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function loadMatching(): TrackerMatching {
|
|
16
|
+
const body = TRACKER_SOURCE.replace(/^export\s+/gm, '')
|
|
17
|
+
const factory = new Function(
|
|
18
|
+
`${body}
|
|
19
|
+
return {
|
|
20
|
+
setExcludedPaths: function (paths) { excludedPaths = paths },
|
|
21
|
+
setLocales: function (locales) { siteLocales = locales },
|
|
22
|
+
isPathExcluded: isPathExcluded,
|
|
23
|
+
normalizeTrackedPath: normalizeTrackedPath,
|
|
24
|
+
}`
|
|
25
|
+
)
|
|
26
|
+
return factory() as TrackerMatching
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('tracked pages — the emitted tracker', () => {
|
|
30
|
+
it('tracks every page when no list was baked in', () => {
|
|
31
|
+
const tracker = loadMatching()
|
|
32
|
+
expect(tracker.isPathExcluded('/admin-panel/orders')).toBe(false)
|
|
33
|
+
expect(tracker.isPathExcluded('/')).toBe(false)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('keeps a listed page out, exactly or by prefix, however the address is written', () => {
|
|
37
|
+
const tracker = loadMatching()
|
|
38
|
+
tracker.setExcludedPaths(['/admin-panel', '/admin-panel/*', '/secret'])
|
|
39
|
+
|
|
40
|
+
expect(tracker.isPathExcluded('/admin-panel')).toBe(true)
|
|
41
|
+
expect(tracker.isPathExcluded('/admin-panel/')).toBe(true)
|
|
42
|
+
expect(tracker.isPathExcluded('/admin-panel/orders/42')).toBe(true)
|
|
43
|
+
expect(tracker.isPathExcluded('/secret?tab=1#top')).toBe(true)
|
|
44
|
+
|
|
45
|
+
expect(tracker.isPathExcluded('/')).toBe(false)
|
|
46
|
+
expect(tracker.isPathExcluded('/secrets')).toBe(false)
|
|
47
|
+
expect(tracker.isPathExcluded('/admin-panels')).toBe(false)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('matches a dynamic page by its published prefix without touching its listing page', () => {
|
|
51
|
+
const tracker = loadMatching()
|
|
52
|
+
tracker.setExcludedPaths(['/products/*'])
|
|
53
|
+
|
|
54
|
+
expect(tracker.isPathExcluded('/products/42')).toBe(true)
|
|
55
|
+
expect(tracker.isPathExcluded('/products')).toBe(false)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('judges a locale-prefixed address against the same list', () => {
|
|
59
|
+
const tracker = loadMatching()
|
|
60
|
+
tracker.setExcludedPaths(['/admin-panel/*', '/'])
|
|
61
|
+
tracker.setLocales(['en', 'fr'])
|
|
62
|
+
|
|
63
|
+
expect(tracker.normalizeTrackedPath('/fr/admin-panel/orders/')).toBe('/admin-panel/orders')
|
|
64
|
+
expect(tracker.isPathExcluded('/fr/admin-panel/orders')).toBe(true)
|
|
65
|
+
expect(tracker.isPathExcluded('/fr')).toBe(true)
|
|
66
|
+
expect(tracker.isPathExcluded('/fr/about')).toBe(false)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('silences a page kept out of analytics instead of giving it a page identity', () => {
|
|
70
|
+
expect(TRACKER_SOURCE).toMatch(
|
|
71
|
+
/if \(isPathExcluded\(newPath\)\) \{[\s\S]*?currentPath = newPath\s*pageLoadId = null\s*return/
|
|
72
|
+
)
|
|
73
|
+
// Commerce steps guard on the page identity like clicks and heartbeats do.
|
|
74
|
+
expect(TRACKER_SOURCE).toContain('if (!initialized || !isTrackingPossible() || !pageLoadId) {')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('click matching — what a funnel step can see', () => {
|
|
79
|
+
it('reports the clicked element’s classes, capped, so ".class" steps match', () => {
|
|
80
|
+
expect(TRACKER_SOURCE).toContain(
|
|
81
|
+
"Array.prototype.slice.call(target.classList).join(' ').slice(0, 255)"
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('counts a click on any element marked data-tp-track', () => {
|
|
86
|
+
expect(TRACKER_SOURCE).toContain(
|
|
87
|
+
`'a, button, [role="button"], input[type="submit"], [data-tp-event], [data-tp-track]'`
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { parse } from '@babel/parser'
|
|
2
|
+
import { GeneratedFolder, ProjectUIDL } from '@teleporthq/teleport-types'
|
|
3
|
+
import { ContentTables } from '@teleporthq/teleport-shared'
|
|
4
|
+
import { generateSafeFileName } from '@teleporthq/teleport-plugin-next-data-source'
|
|
5
|
+
import uidlSample from '../../../examples/test-samples/project-sample.json'
|
|
6
|
+
import { createNextProjectGenerator } from '../src'
|
|
7
|
+
import NextTemplate from '../src/project-template'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A content preset's CATEGORY PAGE end to end: the editor exports one dynamic
|
|
11
|
+
* page at `/help/category/[slug]` whose `getStaticProps` / `getStaticPaths`
|
|
12
|
+
* are EXTERNAL resources — two functions of the `content-category-pages`
|
|
13
|
+
* module the blog project plugin emits — instead of a table fetch. Generated
|
|
14
|
+
* code is the only place that contract proves itself: the page must import
|
|
15
|
+
* the functions by the names teleport-shared derives, call the resolver with
|
|
16
|
+
* the route's slug, expose the result as the page prop the page's bindings
|
|
17
|
+
* read, 404 on an empty answer, and revalidate; the module must exist and read
|
|
18
|
+
* the preset's posts through its generated fetcher.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const template = JSON.parse(JSON.stringify(NextTemplate)) as GeneratedFolder
|
|
22
|
+
|
|
23
|
+
const DATA_SOURCE_ID = 'ds-help-content'
|
|
24
|
+
const TABLE = ContentTables.contentTablesByKey('help').posts
|
|
25
|
+
const MODULE = ContentTables.CONTENT_CATEGORY_PAGES_MODULE
|
|
26
|
+
const RESOLVER = ContentTables.contentCategoryPageResolverName('help')
|
|
27
|
+
const LISTER = ContentTables.contentCategoryPathsListerName('help')
|
|
28
|
+
const ENTITY = 'helpCenterCategory'
|
|
29
|
+
|
|
30
|
+
const dependency = { type: 'local', path: MODULE, meta: { namedImport: true } }
|
|
31
|
+
|
|
32
|
+
const buildUidl = (): ProjectUIDL => {
|
|
33
|
+
const uidl = JSON.parse(JSON.stringify(uidlSample)) as ProjectUIDL
|
|
34
|
+
|
|
35
|
+
uidl.dataSources = {
|
|
36
|
+
[DATA_SOURCE_ID]: {
|
|
37
|
+
id: DATA_SOURCE_ID,
|
|
38
|
+
name: 'Content',
|
|
39
|
+
type: 'teleport',
|
|
40
|
+
config: {
|
|
41
|
+
selectedTables: {
|
|
42
|
+
[TABLE]: {
|
|
43
|
+
name: TABLE,
|
|
44
|
+
columns: [
|
|
45
|
+
{ name: 'id', type: 'uuid' },
|
|
46
|
+
{ name: 'title', type: 'varchar' },
|
|
47
|
+
{ name: 'slug', type: 'varchar' },
|
|
48
|
+
{ name: 'status', type: 'varchar' },
|
|
49
|
+
{ name: 'category_filter_ids', type: 'text' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
} as never
|
|
56
|
+
|
|
57
|
+
// The editor emits the block for every project with a data source; the
|
|
58
|
+
// category page registers no fetcher of its own in it.
|
|
59
|
+
uidl.resources = { items: {}, resourceMappers: {} } as never
|
|
60
|
+
|
|
61
|
+
uidl.helpCenterSettings = {
|
|
62
|
+
categories: [
|
|
63
|
+
{ id: 'guides', name: 'Guides', slug: 'guides', parentId: null, order: 0, children: [] },
|
|
64
|
+
],
|
|
65
|
+
categoryPages: {
|
|
66
|
+
dataSourceId: DATA_SOURCE_ID,
|
|
67
|
+
postPath: '/help',
|
|
68
|
+
postUrlField: 'slug',
|
|
69
|
+
categoryPath: '/help/category',
|
|
70
|
+
},
|
|
71
|
+
} as never
|
|
72
|
+
|
|
73
|
+
const route = {
|
|
74
|
+
value: 'help/category/Help-Category',
|
|
75
|
+
pageId: 'TQ_help_category_page',
|
|
76
|
+
seo: { title: 'Category' },
|
|
77
|
+
pageOptions: {
|
|
78
|
+
stateDefinitions: {},
|
|
79
|
+
propDefinitions: {},
|
|
80
|
+
navLink: '/help/category/[slug]',
|
|
81
|
+
dynamicRouteAttribute: 'slug',
|
|
82
|
+
initialPropsData: {
|
|
83
|
+
exposeAs: { name: ENTITY, valuePath: ['data'] },
|
|
84
|
+
resource: {
|
|
85
|
+
name: RESOLVER,
|
|
86
|
+
dependency,
|
|
87
|
+
params: { slug: { type: 'expr', content: "params['slug']" } },
|
|
88
|
+
},
|
|
89
|
+
cache: { revalidate: 60 },
|
|
90
|
+
},
|
|
91
|
+
initialPathsData: {
|
|
92
|
+
exposeAs: { name: `${ENTITY}Paths`, valuePath: ['data'], itemValuePath: ['slug'] },
|
|
93
|
+
resource: { name: LISTER, dependency },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const pageNode = {
|
|
99
|
+
type: 'conditional',
|
|
100
|
+
content: {
|
|
101
|
+
reference: { type: 'dynamic', content: { referenceType: 'state', id: 'route' } },
|
|
102
|
+
value: 'help/category/Help-Category',
|
|
103
|
+
node: {
|
|
104
|
+
type: 'element',
|
|
105
|
+
content: {
|
|
106
|
+
elementType: 'container',
|
|
107
|
+
children: [
|
|
108
|
+
{
|
|
109
|
+
type: 'element',
|
|
110
|
+
content: {
|
|
111
|
+
elementType: 'text',
|
|
112
|
+
children: [
|
|
113
|
+
{
|
|
114
|
+
type: 'dynamic',
|
|
115
|
+
content: {
|
|
116
|
+
referenceType: 'prop',
|
|
117
|
+
id: ENTITY,
|
|
118
|
+
refPath: ['name'],
|
|
119
|
+
fallback: '',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
uidl.root.stateDefinitions?.route.values?.push(route as never)
|
|
132
|
+
uidl.root.node.content.children?.push(pageNode as never)
|
|
133
|
+
return uidl
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const findFile = (folder: GeneratedFolder, path: string[]): string => {
|
|
137
|
+
let current: GeneratedFolder | undefined = folder
|
|
138
|
+
for (const segment of path.slice(0, -1)) {
|
|
139
|
+
current = current?.subFolders.find((sub) => sub.name === segment)
|
|
140
|
+
}
|
|
141
|
+
const file = current?.files.find((entry) => entry.name === path[path.length - 1])
|
|
142
|
+
expect(file).toBeDefined()
|
|
143
|
+
return file?.content as string
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const expectValidJs = (source: string) => {
|
|
147
|
+
expect(() => parse(source, { sourceType: 'module', plugins: ['jsx'] })).not.toThrow()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
describe('a content preset category page, generated', () => {
|
|
151
|
+
let page: string
|
|
152
|
+
let module: string
|
|
153
|
+
let blogContext: string
|
|
154
|
+
|
|
155
|
+
beforeAll(async () => {
|
|
156
|
+
const generator = createNextProjectGenerator()
|
|
157
|
+
const output = await generator.generateProject(buildUidl(), template)
|
|
158
|
+
page = findFile(output, ['pages', 'help', 'category', '[slug]'])
|
|
159
|
+
module = findFile(output, ['content-category-pages'])
|
|
160
|
+
blogContext = findFile(output, ['blog-context'])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('resolves the category at the slug through the generated module, as the page prop', () => {
|
|
164
|
+
expectValidJs(page)
|
|
165
|
+
expect(page).toMatch(
|
|
166
|
+
new RegExp(
|
|
167
|
+
`import \\{[\\s\\S]*\\b${RESOLVER}\\b[\\s\\S]*\\} from '${MODULE.replace('/', '\\/')}'`
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
expect(page).toMatch(new RegExp(`await ${RESOLVER}\\(\\{[\\s\\S]*slug: params\\['slug'\\]`))
|
|
171
|
+
expect(page).toMatch(
|
|
172
|
+
/if \(!response\?\.data\) \{\s*return \{\s*notFound: true,\s*revalidate: 60/
|
|
173
|
+
)
|
|
174
|
+
expect(page).toContain(`${ENTITY}: response?.data`)
|
|
175
|
+
// The page's text reads the prop the resolver exposed, however the
|
|
176
|
+
// generator spells the optional chain.
|
|
177
|
+
expect(page).toMatch(new RegExp(`props\\.${ENTITY}\\??\\.(\\['name'\\]|name)`))
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('lists every category slug for getStaticPaths through the module', () => {
|
|
181
|
+
expect(page).toMatch(
|
|
182
|
+
new RegExp(
|
|
183
|
+
`import \\{[\\s\\S]*\\b${LISTER}\\b[\\s\\S]*\\} from '${MODULE.replace('/', '\\/')}'`
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
expect(page).toContain(`await ${LISTER}(`)
|
|
187
|
+
expect(page).toContain("fallback: 'blocking'")
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('emits the module over the baked taxonomy and the preset’s posts fetcher', () => {
|
|
191
|
+
expectValidJs(module)
|
|
192
|
+
expect(module).toContain(`export async function ${RESOLVER}(params)`)
|
|
193
|
+
expect(module).toContain(`export async function ${LISTER}()`)
|
|
194
|
+
expect(module).toContain(
|
|
195
|
+
`import helpPosts from './utils/data-sources/${generateSafeFileName(
|
|
196
|
+
'teleport',
|
|
197
|
+
TABLE,
|
|
198
|
+
DATA_SOURCE_ID
|
|
199
|
+
)}'`
|
|
200
|
+
)
|
|
201
|
+
expect(module).toContain("from '@/blog-context'")
|
|
202
|
+
expect(blogContext).toContain('export const HELP_CATEGORY_TREE')
|
|
203
|
+
expect(blogContext).toContain('export const localizeCategoryTree')
|
|
204
|
+
})
|
|
205
|
+
})
|