docusaurus-plugin-generate-schema-docs 1.1.0 → 1.2.0
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/README.md +97 -0
- package/__tests__/ExampleDataLayer.test.js +169 -0
- package/__tests__/__fixtures__/static/schemas/add-to-cart-event.json +55 -0
- package/__tests__/__fixtures__/static/schemas/components/dataLayer.json +58 -0
- package/__tests__/__fixtures__/static/schemas/components/product.json +211 -0
- package/__tests__/__fixtures__/validateSchemas/circular-schema.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/components/referenced.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/invalid-example-schema.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-missing-ref.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-ref.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/no-example-schema.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-A.json +7 -0
- package/__tests__/__fixtures__/validateSchemas/schema-B.json +7 -0
- package/__tests__/__fixtures__/validateSchemas/valid-schema.json +8 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +53 -0
- package/__tests__/components/PropertiesTable.test.js +24 -0
- package/__tests__/components/PropertyRow.test.js +67 -0
- package/__tests__/components/SchemaJsonViewer.test.js +32 -0
- package/__tests__/components/SchemaRows.test.js +80 -0
- package/__tests__/components/SchemaViewer.test.js +27 -0
- package/__tests__/components/TableHeader.test.js +20 -0
- package/__tests__/generateEventDocs.test.js +84 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +188 -0
- package/__tests__/helpers/getConstraints.test.js +58 -0
- package/__tests__/helpers/loadSchema.test.js +14 -0
- package/__tests__/helpers/processSchema.test.js +36 -0
- package/__tests__/validateSchemas.test.js +100 -0
- package/components/ExampleDataLayer.js +7 -5
- package/components/PropertiesTable.js +5 -10
- package/components/PropertyRow.js +74 -0
- package/components/SchemaJsonViewer.js +3 -2
- package/components/SchemaRows.css +28 -0
- package/components/SchemaRows.js +19 -47
- package/components/SchemaViewer.js +4 -2
- package/components/TableHeader.js +15 -0
- package/generateEventDocs.js +37 -36
- package/helpers/buildExampleFromSchema.js +3 -3
- package/helpers/getConstraints.js +53 -0
- package/helpers/loadSchema.js +11 -0
- package/helpers/mdx-template.js +36 -0
- package/helpers/processSchema.js +32 -0
- package/index.js +21 -11
- package/package.json +13 -2
- package/validateSchemas.js +6 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
|
+
|
|
3
|
+
exports[`generateEventDocs should generate documentation correctly when no partials exist 1`] = `
|
|
4
|
+
"---
|
|
5
|
+
title: Add to Cart Event
|
|
6
|
+
description: "An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications."
|
|
7
|
+
sidebar_label: Add to Cart Event
|
|
8
|
+
custom_edit_url: https://github.com/test-org/test-project/edit/main/demo/static/schemas/add-to-cart-event.json
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
import SchemaViewer from '@theme/SchemaViewer';
|
|
12
|
+
import SchemaJsonViewer from '@theme/SchemaJsonViewer';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Add to Cart Event
|
|
17
|
+
|
|
18
|
+
An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
<SchemaViewer schema={{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/add-to-cart-event.json","title":"Add to Cart Event","description":"An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.","type":"object","required":["event"],"$defs":{"strictObject":{"type":"object","propertyNames":{"maxLength":40,"pattern":"^[a-z0-9_]+$"},"additionalProperties":{"anyOf":[{"$ref":"#/allOf/0/$defs/strictObject"},{"type":"array","items":{"$ref":"#/allOf/0/$defs/strictObject"}},{"not":{"type":"object"}}]}}},"$ref":"#/allOf/0/$defs/strictObject","properties":{"event":{"type":"string","const":"add_to_cart","description":"The name of the event indicating a add_to_cart has occurred."},"ecommerce":{"type":"object","description":"Ecommerce related data for the purchase event.","required":["currency","items"],"x-gtm-clear":true,"properties":{"value":{"type":"number","description":"The monetary value of the event. Set value to the sum of (price * quantity) for all items in items.","examples":[30.03,99.99,45.5]},"currency":{"type":"string","description":"The currency of the items in ISO 4217 three-letter format. Required when value is set.","examples":["USD","EUR","GBP"]},"items":{"type":"array","description":"The products added to the cart.","minItems":1,"items":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/components/product.json","title":"A Product","description":"A product object representing an item for use within a purchase event, based on Google Analytics 4 ecommerce event specifications.","type":"object","required":[],"properties":{"item_id":{"type":"string","description":"The ID of the item. Either item_id or item_name is required.","examples":["SKU_12345","PROD_98765"]},"item_name":{"type":"string","description":"The name of the item. Either item_id or item_name is required.","examples":["Stan and Friends Tee","Blue Running Shoes"]},"affiliation":{"type":"string","description":"A product affiliation to indicate a supplier or store. Note: affiliation is only available at the item level.","examples":["Google Merchandise Store","Nike Store"]},"coupon":{"type":"string","description":"The coupon name/code associated with the item. Coupon parameters at event and item levels are independent.","examples":["SUMMER_FUN","WELCOME20","SAVE10"]},"discount":{"type":"number","description":"The monetary value of the discount per unit applied to the item.","examples":[2.22,5,10.5]},"index":{"type":"number","description":"The index or position of the item in a list.","examples":[0,1,2]},"item_brand":{"type":"string","description":"The brand of the item.","examples":["Google","Nike","Adidas"]},"item_category":{"type":"string","description":"The category of the item. If used as part of a category hierarchy or taxonomy, this is the first category.","examples":["Apparel","Footwear","Electronics"]},"item_category2":{"type":"string","description":"The second category hierarchy or additional taxonomy of the item.","examples":["Adult","Women","Men"]},"item_category3":{"type":"string","description":"The third category hierarchy or additional taxonomy of the item.","examples":["Shirts","Shoes","Boots"]},"item_category4":{"type":"string","description":"The fourth category hierarchy or additional taxonomy of the item.","examples":["Crew","Running","Casual"]},"item_category5":{"type":"string","description":"The fifth category hierarchy or additional taxonomy of the item.","examples":["Short sleeve","Long distance","Lightweight"]},"item_list_id":{"type":"string","description":"The ID of the list in which the item was presented to the user. If set, item_list_id at the event level is ignored. If not set, item_list_id at the event level is used if available.","examples":["related_products","search_results","recommendations"]},"item_list_name":{"type":"string","description":"The name of the list in which the item was presented to the user. If set, item_list_name at the event level is ignored. If not set, item_list_name at the event level is used if available.","examples":["Related Products","Search Results","Recommended Items"]},"item_variant":{"type":"string","description":"The item variant or unique code or description for additional item details/options.","examples":["green","Blue Size 10","M-Black"]},"location_id":{"type":"string","description":"The physical location associated with the item, such as the location of a brick-and-mortar store. It is recommended to use the Google Place ID that corresponds to the associated item. A custom location ID can also be used. Note: location_id is only available at the item level.","examples":["ChIJIQBpAG2ahYAR_6128GcTUEo","store_123","location_sf"]},"price":{"type":"number","description":"The price of the item in the specified currency unit. If a discount is applied to the item, set price to the reduced per-unit price and provide the per-unit price discount in the discount parameter.","examples":[10.01,21.01,89.99]},"quantity":{"type":"number","description":"The item quantity. If not set, quantity is set to 1.","examples":[1,2,3]},"promotion_id":{"type":"string","description":"The ID of the promotion associated with the item.","examples":["P_12345","PROMO_001"]},"promotion_name":{"type":"string","description":"The name of the promotion associated with the item.","examples":["Summer Sale","Black Friday","Flash Deal"]},"creative_name":{"type":"string","description":"The name of the promotion creative.","examples":["summer_banner2","holiday_email_v1"]},"creative_slot":{"type":"string","description":"The name of the creative slot associated with the item.","examples":["featured_app_1","top_banner","sidebar"]},"google_business_vertical":{"type":"string","description":"The business vertical for the item (e.g., 'retail').","examples":["retail","ecommerce"]}}}}}},"user_data":{"type":"object","description":"User related data.","x-gtm-clear":true}}}} />
|
|
23
|
+
<SchemaJsonViewer schema={{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/add-to-cart-event.json","title":"Add to Cart Event","description":"An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.","type":"object","allOf":[{"$ref":"./components/dataLayer.json"}],"properties":{"event":{"type":"string","const":"add_to_cart","description":"The name of the event indicating a add_to_cart has occurred."},"ecommerce":{"type":"object","description":"Ecommerce related data for the purchase event.","properties":{"value":{"type":"number","description":"The monetary value of the event. Set value to the sum of (price * quantity) for all items in items.","examples":[30.03,99.99,45.5]},"currency":{"type":"string","description":"The currency of the items in ISO 4217 three-letter format. Required when value is set.","examples":["USD","EUR","GBP"]},"items":{"type":"array","description":"The products added to the cart.","minItems":1,"items":{"$ref":"./components/product.json"}}},"required":["currency","items"]}}}} />
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
"
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`generateEventDocs should generate documentation with top and bottom partials when they exist 1`] = `
|
|
30
|
+
"---
|
|
31
|
+
title: Add to Cart Event
|
|
32
|
+
description: "An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications."
|
|
33
|
+
sidebar_label: Add to Cart Event
|
|
34
|
+
custom_edit_url: https://github.com/test-org/test-project/edit/main/demo/static/schemas/add-to-cart-event.json
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
import SchemaViewer from '@theme/SchemaViewer';
|
|
38
|
+
import SchemaJsonViewer from '@theme/SchemaJsonViewer';
|
|
39
|
+
import TopPartial from '@site/docs/partials/add-to-cart-event.mdx';
|
|
40
|
+
import BottomPartial from '@site/docs/partials/add-to-cart-event_bottom.mdx';
|
|
41
|
+
|
|
42
|
+
# Add to Cart Event
|
|
43
|
+
|
|
44
|
+
An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.
|
|
45
|
+
|
|
46
|
+
<TopPartial />
|
|
47
|
+
|
|
48
|
+
<SchemaViewer schema={{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/add-to-cart-event.json","title":"Add to Cart Event","description":"An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.","type":"object","required":["event"],"$defs":{"strictObject":{"type":"object","propertyNames":{"maxLength":40,"pattern":"^[a-z0-9_]+$"},"additionalProperties":{"anyOf":[{"$ref":"#/allOf/0/$defs/strictObject"},{"type":"array","items":{"$ref":"#/allOf/0/$defs/strictObject"}},{"not":{"type":"object"}}]}}},"$ref":"#/allOf/0/$defs/strictObject","properties":{"event":{"type":"string","const":"add_to_cart","description":"The name of the event indicating a add_to_cart has occurred."},"ecommerce":{"type":"object","description":"Ecommerce related data for the purchase event.","required":["currency","items"],"x-gtm-clear":true,"properties":{"value":{"type":"number","description":"The monetary value of the event. Set value to the sum of (price * quantity) for all items in items.","examples":[30.03,99.99,45.5]},"currency":{"type":"string","description":"The currency of the items in ISO 4217 three-letter format. Required when value is set.","examples":["USD","EUR","GBP"]},"items":{"type":"array","description":"The products added to the cart.","minItems":1,"items":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/components/product.json","title":"A Product","description":"A product object representing an item for use within a purchase event, based on Google Analytics 4 ecommerce event specifications.","type":"object","required":[],"properties":{"item_id":{"type":"string","description":"The ID of the item. Either item_id or item_name is required.","examples":["SKU_12345","PROD_98765"]},"item_name":{"type":"string","description":"The name of the item. Either item_id or item_name is required.","examples":["Stan and Friends Tee","Blue Running Shoes"]},"affiliation":{"type":"string","description":"A product affiliation to indicate a supplier or store. Note: affiliation is only available at the item level.","examples":["Google Merchandise Store","Nike Store"]},"coupon":{"type":"string","description":"The coupon name/code associated with the item. Coupon parameters at event and item levels are independent.","examples":["SUMMER_FUN","WELCOME20","SAVE10"]},"discount":{"type":"number","description":"The monetary value of the discount per unit applied to the item.","examples":[2.22,5,10.5]},"index":{"type":"number","description":"The index or position of the item in a list.","examples":[0,1,2]},"item_brand":{"type":"string","description":"The brand of the item.","examples":["Google","Nike","Adidas"]},"item_category":{"type":"string","description":"The category of the item. If used as part of a category hierarchy or taxonomy, this is the first category.","examples":["Apparel","Footwear","Electronics"]},"item_category2":{"type":"string","description":"The second category hierarchy or additional taxonomy of the item.","examples":["Adult","Women","Men"]},"item_category3":{"type":"string","description":"The third category hierarchy or additional taxonomy of the item.","examples":["Shirts","Shoes","Boots"]},"item_category4":{"type":"string","description":"The fourth category hierarchy or additional taxonomy of the item.","examples":["Crew","Running","Casual"]},"item_category5":{"type":"string","description":"The fifth category hierarchy or additional taxonomy of the item.","examples":["Short sleeve","Long distance","Lightweight"]},"item_list_id":{"type":"string","description":"The ID of the list in which the item was presented to the user. If set, item_list_id at the event level is ignored. If not set, item_list_id at the event level is used if available.","examples":["related_products","search_results","recommendations"]},"item_list_name":{"type":"string","description":"The name of the list in which the item was presented to the user. If set, item_list_name at the event level is ignored. If not set, item_list_name at the event level is used if available.","examples":["Related Products","Search Results","Recommended Items"]},"item_variant":{"type":"string","description":"The item variant or unique code or description for additional item details/options.","examples":["green","Blue Size 10","M-Black"]},"location_id":{"type":"string","description":"The physical location associated with the item, such as the location of a brick-and-mortar store. It is recommended to use the Google Place ID that corresponds to the associated item. A custom location ID can also be used. Note: location_id is only available at the item level.","examples":["ChIJIQBpAG2ahYAR_6128GcTUEo","store_123","location_sf"]},"price":{"type":"number","description":"The price of the item in the specified currency unit. If a discount is applied to the item, set price to the reduced per-unit price and provide the per-unit price discount in the discount parameter.","examples":[10.01,21.01,89.99]},"quantity":{"type":"number","description":"The item quantity. If not set, quantity is set to 1.","examples":[1,2,3]},"promotion_id":{"type":"string","description":"The ID of the promotion associated with the item.","examples":["P_12345","PROMO_001"]},"promotion_name":{"type":"string","description":"The name of the promotion associated with the item.","examples":["Summer Sale","Black Friday","Flash Deal"]},"creative_name":{"type":"string","description":"The name of the promotion creative.","examples":["summer_banner2","holiday_email_v1"]},"creative_slot":{"type":"string","description":"The name of the creative slot associated with the item.","examples":["featured_app_1","top_banner","sidebar"]},"google_business_vertical":{"type":"string","description":"The business vertical for the item (e.g., 'retail').","examples":["retail","ecommerce"]}}}}}},"user_data":{"type":"object","description":"User related data.","x-gtm-clear":true}}}} />
|
|
49
|
+
<SchemaJsonViewer schema={{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://tracking-docs-demo.buchert.digital/schemas/add-to-cart-event.json","title":"Add to Cart Event","description":"An add_to_cart event fires when a user adds one or more items to their shopping cart. Based on Google Analytics 4 ecommerce event specifications.","type":"object","allOf":[{"$ref":"./components/dataLayer.json"}],"properties":{"event":{"type":"string","const":"add_to_cart","description":"The name of the event indicating a add_to_cart has occurred."},"ecommerce":{"type":"object","description":"Ecommerce related data for the purchase event.","properties":{"value":{"type":"number","description":"The monetary value of the event. Set value to the sum of (price * quantity) for all items in items.","examples":[30.03,99.99,45.5]},"currency":{"type":"string","description":"The currency of the items in ISO 4217 three-letter format. Required when value is set.","examples":["USD","EUR","GBP"]},"items":{"type":"array","description":"The products added to the cart.","minItems":1,"items":{"$ref":"./components/product.json"}}},"required":["currency","items"]}}}} />
|
|
50
|
+
|
|
51
|
+
<BottomPartial />
|
|
52
|
+
"
|
|
53
|
+
`;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import PropertiesTable from '../../components/PropertiesTable';
|
|
5
|
+
|
|
6
|
+
// Mock child components
|
|
7
|
+
jest.mock('../../components/TableHeader', () => () => <thead><tr><th>Mocked TableHeader</th></tr></thead>);
|
|
8
|
+
jest.mock('../../components/SchemaRows', () => () => <tr><td>Mocked SchemaRows</td></tr>);
|
|
9
|
+
|
|
10
|
+
describe('PropertiesTable', () => {
|
|
11
|
+
it('renders the table with header and schema rows', () => {
|
|
12
|
+
const schema = {
|
|
13
|
+
properties: {
|
|
14
|
+
name: { type: 'string' },
|
|
15
|
+
},
|
|
16
|
+
required: ['name'],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const { getByText } = render(<PropertiesTable schema={schema} />);
|
|
20
|
+
|
|
21
|
+
expect(getByText('Mocked TableHeader')).toBeInTheDocument();
|
|
22
|
+
expect(getByText('Mocked SchemaRows')).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import PropertyRow from '../../components/PropertyRow';
|
|
5
|
+
|
|
6
|
+
describe('PropertyRow', () => {
|
|
7
|
+
const getConstraints = (prop, isReq) => {
|
|
8
|
+
const constraints = [];
|
|
9
|
+
if (isReq) constraints.push('required');
|
|
10
|
+
if (prop.minLength) constraints.push(`minLength: ${prop.minLength}`);
|
|
11
|
+
if (prop.maxLength) constraints.push(`maxLength: ${prop.maxLength}`);
|
|
12
|
+
return constraints;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it('renders a basic property', () => {
|
|
16
|
+
const prop = { type: 'string', description: 'The name of the user.' };
|
|
17
|
+
const { getByText } = render(
|
|
18
|
+
<table>
|
|
19
|
+
<tbody>
|
|
20
|
+
<PropertyRow propertyKey="name" prop={prop} requiredList={[]} getConstraints={getConstraints} />
|
|
21
|
+
</tbody>
|
|
22
|
+
</table>
|
|
23
|
+
);
|
|
24
|
+
expect(getByText('name')).toBeInTheDocument();
|
|
25
|
+
expect(getByText('string')).toBeInTheDocument();
|
|
26
|
+
expect(getByText('The name of the user.')).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('marks required properties', () => {
|
|
30
|
+
const prop = { type: 'string' };
|
|
31
|
+
const { container } = render(
|
|
32
|
+
<table>
|
|
33
|
+
<tbody>
|
|
34
|
+
<PropertyRow propertyKey="name" prop={prop} requiredList={['name']} getConstraints={getConstraints} />
|
|
35
|
+
</tbody>
|
|
36
|
+
</table>
|
|
37
|
+
);
|
|
38
|
+
expect(container.querySelector('.required-row')).toBeInTheDocument();
|
|
39
|
+
expect(container.querySelector('.required')).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders multiple constraints', () => {
|
|
43
|
+
const prop = { type: 'string', minLength: 1, maxLength: 10 };
|
|
44
|
+
const { getByText } = render(
|
|
45
|
+
<table>
|
|
46
|
+
<tbody>
|
|
47
|
+
<PropertyRow propertyKey="name" prop={prop} requiredList={['name']} getConstraints={getConstraints} />
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
50
|
+
);
|
|
51
|
+
expect(getByText('required')).toBeInTheDocument();
|
|
52
|
+
expect(getByText('minLength: 1')).toBeInTheDocument();
|
|
53
|
+
expect(getByText('maxLength: 10')).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('formats examples', () => {
|
|
57
|
+
const prop = { type: 'string', examples: ['foo', 'bar'] };
|
|
58
|
+
const { getByText } = render(
|
|
59
|
+
<table>
|
|
60
|
+
<tbody>
|
|
61
|
+
<PropertyRow propertyKey="name" prop={prop} requiredList={[]} getConstraints={getConstraints} />
|
|
62
|
+
</tbody>
|
|
63
|
+
</table>
|
|
64
|
+
);
|
|
65
|
+
expect(getByText('foo, bar')).toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import SchemaJsonViewer from '../../components/SchemaJsonViewer';
|
|
5
|
+
|
|
6
|
+
// Mock CodeBlock as it's an external theme component
|
|
7
|
+
jest.mock('@theme/CodeBlock', () => {
|
|
8
|
+
return function DummyCodeBlock({ children, language }) {
|
|
9
|
+
return <pre data-language={language}>{children}</pre>;
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('SchemaJsonViewer', () => {
|
|
14
|
+
it('renders the schema in a CodeBlock', () => {
|
|
15
|
+
const schema = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
name: { type: 'string' },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const { getByText, container } = render(<SchemaJsonViewer schema={schema} />);
|
|
23
|
+
|
|
24
|
+
expect(getByText('View Raw JSON Schema')).toBeInTheDocument();
|
|
25
|
+
const detailsElement = container.querySelector('details');
|
|
26
|
+
expect(detailsElement).toHaveClass('schema-json-viewer');
|
|
27
|
+
const codeBlockElement = container.querySelector('pre');
|
|
28
|
+
expect(codeBlockElement).toBeInTheDocument();
|
|
29
|
+
expect(codeBlockElement).toHaveAttribute('data-language', 'json');
|
|
30
|
+
expect(codeBlockElement.textContent).toEqual(JSON.stringify(schema, null, 2));
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import SchemaRows from '../../components/SchemaRows';
|
|
5
|
+
|
|
6
|
+
// Mock child components
|
|
7
|
+
jest.mock('../../components/PropertyRow', () => (props) => <tr><td>Mocked PropertyRow: {props.propertyKey}</td></tr>);
|
|
8
|
+
jest.mock('../../components/TableHeader', () => () => <thead><tr><th>Mocked TableHeader</th></tr></thead>);
|
|
9
|
+
|
|
10
|
+
describe('SchemaRows', () => {
|
|
11
|
+
it('renders a PropertyRow for each property', () => {
|
|
12
|
+
const properties = {
|
|
13
|
+
name: { type: 'string' },
|
|
14
|
+
age: { type: 'integer' },
|
|
15
|
+
};
|
|
16
|
+
const { getByText } = render(
|
|
17
|
+
<table>
|
|
18
|
+
<tbody>
|
|
19
|
+
<SchemaRows properties={properties} getConstraints={() => []} />
|
|
20
|
+
</tbody>
|
|
21
|
+
</table>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
expect(getByText('Mocked PropertyRow: name')).toBeInTheDocument();
|
|
25
|
+
expect(getByText('Mocked PropertyRow: age')).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('recursively renders for nested objects', () => {
|
|
29
|
+
const properties = {
|
|
30
|
+
user: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
name: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const { getByText } = render(
|
|
38
|
+
<table>
|
|
39
|
+
<tbody>
|
|
40
|
+
<SchemaRows properties={properties} getConstraints={() => []} />
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(getByText('Mocked PropertyRow: user')).toBeInTheDocument();
|
|
46
|
+
expect(getByText('Mocked TableHeader')).toBeInTheDocument();
|
|
47
|
+
expect(getByText('user { }')).toBeInTheDocument();
|
|
48
|
+
// This will be inside the nested table
|
|
49
|
+
expect(getByText('Mocked PropertyRow: name')).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('recursively renders for nested arrays of objects', () => {
|
|
53
|
+
const properties = {
|
|
54
|
+
products: {
|
|
55
|
+
type: 'array',
|
|
56
|
+
items: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
name: { type: 'string' },
|
|
60
|
+
price: { type: 'number' },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const { getByText } = render(
|
|
66
|
+
<table>
|
|
67
|
+
<tbody>
|
|
68
|
+
<SchemaRows properties={properties} getConstraints={() => []} />
|
|
69
|
+
</tbody>
|
|
70
|
+
</table>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(getByText('Mocked PropertyRow: products')).toBeInTheDocument();
|
|
74
|
+
expect(getByText('Mocked TableHeader')).toBeInTheDocument();
|
|
75
|
+
expect(getByText('products [ ]')).toBeInTheDocument();
|
|
76
|
+
// These will be inside the nested table
|
|
77
|
+
expect(getByText('Mocked PropertyRow: name')).toBeInTheDocument();
|
|
78
|
+
expect(getByText('Mocked PropertyRow: price')).toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import SchemaViewer from '../../components/SchemaViewer';
|
|
5
|
+
|
|
6
|
+
// Mock child components
|
|
7
|
+
|
|
8
|
+
jest.mock('../../components/ExampleDataLayer', () => () => <div>Mocked ExampleDataLayer</div>);
|
|
9
|
+
jest.mock('../../components/PropertiesTable', () => () => <div>Mocked PropertiesTable</div>);
|
|
10
|
+
|
|
11
|
+
describe('SchemaViewer', () => {
|
|
12
|
+
it('renders the schema viewer with all child components', () => {
|
|
13
|
+
const schema = {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
name: { type: 'string' },
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const { getByText } = render(<SchemaViewer schema={schema} />);
|
|
21
|
+
|
|
22
|
+
expect(getByText('DataLayer Example')).toBeInTheDocument();
|
|
23
|
+
expect(getByText('Mocked ExampleDataLayer')).toBeInTheDocument();
|
|
24
|
+
expect(getByText('Event Properties')).toBeInTheDocument();
|
|
25
|
+
expect(getByText('Mocked PropertiesTable')).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import TableHeader from '../../components/TableHeader';
|
|
5
|
+
|
|
6
|
+
describe('TableHeader', () => {
|
|
7
|
+
it('renders the table header with correct columns', () => {
|
|
8
|
+
const { getByText } = render(
|
|
9
|
+
<table>
|
|
10
|
+
<TableHeader />
|
|
11
|
+
</table>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
expect(getByText('Property')).toBeInTheDocument();
|
|
15
|
+
expect(getByText('Type')).toBeInTheDocument();
|
|
16
|
+
expect(getByText('Constraints')).toBeInTheDocument();
|
|
17
|
+
expect(getByText('Examples')).toBeInTheDocument();
|
|
18
|
+
expect(getByText('Description')).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import generateEventDocs from '../generateEventDocs';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
jest.mock('fs', () => {
|
|
10
|
+
const originalFs = jest.requireActual('fs');
|
|
11
|
+
return {
|
|
12
|
+
...originalFs,
|
|
13
|
+
writeFileSync: jest.fn(),
|
|
14
|
+
mkdirSync: jest.fn(),
|
|
15
|
+
existsSync: jest.fn(),
|
|
16
|
+
readdirSync: originalFs.readdirSync,
|
|
17
|
+
readFileSync: originalFs.readFileSync,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('generateEventDocs', () => {
|
|
22
|
+
|
|
23
|
+
const options = {
|
|
24
|
+
organizationName: 'test-org',
|
|
25
|
+
projectName: 'test-project',
|
|
26
|
+
// Use the fixtures directory as the siteDir for tests
|
|
27
|
+
siteDir: path.resolve(__dirname, '__fixtures__')
|
|
28
|
+
}
|
|
29
|
+
const outputDir = path.join(options.siteDir, 'docs/events');
|
|
30
|
+
const partialsDir = path.join(options.siteDir, 'docs/partials');
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
// Clear all instances and calls to constructor and all methods:
|
|
35
|
+
fs.writeFileSync.mockClear();
|
|
36
|
+
fs.mkdirSync.mockClear();
|
|
37
|
+
fs.existsSync.mockClear();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should generate documentation correctly when no partials exist', async () => {
|
|
41
|
+
console.log = jest.fn(); // suppress console.log
|
|
42
|
+
|
|
43
|
+
// Simulate that no partials exist
|
|
44
|
+
fs.existsSync.mockReturnValue(false);
|
|
45
|
+
|
|
46
|
+
await generateEventDocs(options);
|
|
47
|
+
|
|
48
|
+
// Expect writeFileSync to have been called once for each schema
|
|
49
|
+
expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
|
|
50
|
+
|
|
51
|
+
// Check the content of the generated file
|
|
52
|
+
const [filePath, content] = fs.writeFileSync.mock.calls[0];
|
|
53
|
+
expect(filePath).toBe(path.join(outputDir, 'add-to-cart-event.mdx'));
|
|
54
|
+
expect(content).toMatchSnapshot();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should generate documentation with top and bottom partials when they exist', async () => {
|
|
58
|
+
console.log = jest.fn(); // suppress console.log
|
|
59
|
+
|
|
60
|
+
// Simulate that the output directory and partials exist
|
|
61
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
62
|
+
if (filePath === outputDir) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (filePath === path.join(partialsDir, 'add-to-cart-event.mdx')) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (filePath === path.join(partialsDir, 'add-to-cart-event_bottom.mdx')) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await generateEventDocs(options);
|
|
75
|
+
|
|
76
|
+
// Expect writeFileSync to have been called once for each schema
|
|
77
|
+
expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
|
|
78
|
+
|
|
79
|
+
// Check the content of the generated file
|
|
80
|
+
const [filePath, content] = fs.writeFileSync.mock.calls[0];
|
|
81
|
+
expect(filePath).toBe(path.join(outputDir, 'add-to-cart-event.mdx'));
|
|
82
|
+
expect(content).toMatchSnapshot();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import buildExampleFromSchema from '../../helpers/buildExampleFromSchema.js';
|
|
2
|
+
|
|
3
|
+
describe('buildExampleFromSchema', () => {
|
|
4
|
+
it('should build a basic example from a schema', () => {
|
|
5
|
+
const schema = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
name: { type: 'string' },
|
|
9
|
+
age: { type: 'integer' },
|
|
10
|
+
isStudent: { type: 'boolean' },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const example = buildExampleFromSchema(schema);
|
|
15
|
+
|
|
16
|
+
expect(example).toEqual({
|
|
17
|
+
name: '',
|
|
18
|
+
age: 0,
|
|
19
|
+
isStudent: false,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle nested objects and arrays', () => {
|
|
24
|
+
const schema = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
user: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
name: { type: 'string' },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
posts: {
|
|
34
|
+
type: 'array',
|
|
35
|
+
items: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
title: { type: 'string' },
|
|
39
|
+
tags: {
|
|
40
|
+
type: 'array',
|
|
41
|
+
items: { type: 'string' },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const example = buildExampleFromSchema(schema);
|
|
50
|
+
|
|
51
|
+
expect(example).toEqual({
|
|
52
|
+
user: {
|
|
53
|
+
name: '',
|
|
54
|
+
},
|
|
55
|
+
posts: [
|
|
56
|
+
{
|
|
57
|
+
title: '',
|
|
58
|
+
tags: [''],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should use examples, const, and default values', () => {
|
|
65
|
+
const schema = {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
name: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
examples: ['John Doe'],
|
|
71
|
+
},
|
|
72
|
+
role: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
const: 'admin',
|
|
75
|
+
},
|
|
76
|
+
level: {
|
|
77
|
+
type: 'integer',
|
|
78
|
+
default: 1,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const example = buildExampleFromSchema(schema);
|
|
84
|
+
|
|
85
|
+
expect(example).toEqual({
|
|
86
|
+
name: 'John Doe',
|
|
87
|
+
role: 'admin',
|
|
88
|
+
level: 1,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should build a complex example', () => {
|
|
93
|
+
const schema = {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
event: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
examples: ['purchase'],
|
|
99
|
+
},
|
|
100
|
+
ecommerce: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
transaction_id: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
examples: ['T_12345'],
|
|
106
|
+
},
|
|
107
|
+
value: {
|
|
108
|
+
type: 'number',
|
|
109
|
+
default: 10.0,
|
|
110
|
+
},
|
|
111
|
+
items: {
|
|
112
|
+
type: 'array',
|
|
113
|
+
items: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
item_id: { type: 'string', examples: ['SKU_123'] },
|
|
117
|
+
item_name: { type: 'string', examples: ['Stan Smith Shoes'] },
|
|
118
|
+
price: { type: 'number' },
|
|
119
|
+
quantity: { type: 'integer', default: 1 },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const example = buildExampleFromSchema(schema);
|
|
129
|
+
|
|
130
|
+
expect(example).toEqual({
|
|
131
|
+
event: 'purchase',
|
|
132
|
+
ecommerce: {
|
|
133
|
+
transaction_id: 'T_12345',
|
|
134
|
+
value: 10.0,
|
|
135
|
+
items: [
|
|
136
|
+
{
|
|
137
|
+
item_id: 'SKU_123',
|
|
138
|
+
item_name: 'Stan Smith Shoes',
|
|
139
|
+
price: 0,
|
|
140
|
+
quantity: 1,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
it('should not include empty objects for properties with no defined value', () => {
|
|
147
|
+
const schema = {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
event: { type: 'string', examples: ['test_event'] },
|
|
151
|
+
user_data: {
|
|
152
|
+
type: 'object'
|
|
153
|
+
},
|
|
154
|
+
ecommerce: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
items: {
|
|
158
|
+
type: 'array',
|
|
159
|
+
items: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
item_id: { type: 'string', examples: ['SKU_123'] }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
empty_object: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const example = buildExampleFromSchema(schema);
|
|
176
|
+
|
|
177
|
+
expect(example).toEqual({
|
|
178
|
+
event: 'test_event',
|
|
179
|
+
ecommerce: {
|
|
180
|
+
items: [
|
|
181
|
+
{
|
|
182
|
+
item_id: 'SKU_123',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|