gt-sanity 0.0.6 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/dist/index.d.mts +95 -73
- package/dist/index.d.ts +95 -73
- package/dist/index.js +6233 -1162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6292 -1193
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
- package/src/adapter/core.ts +41 -4
- package/src/adapter/getLocales.ts +2 -2
- package/src/components/TranslationsProvider.tsx +942 -0
- package/src/components/page/BatchProgress.tsx +27 -0
- package/src/components/page/ImportAllDialog.tsx +51 -0
- package/src/components/page/ImportMissingDialog.tsx +55 -0
- package/src/components/page/TranslateAllDialog.tsx +55 -0
- package/src/components/page/TranslationsTable.tsx +81 -0
- package/src/components/page/TranslationsTool.tsx +299 -837
- package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
- package/src/components/shared/LocaleCheckbox.tsx +47 -0
- package/src/components/shared/SingleDocumentView.tsx +108 -0
- package/src/components/tab/TranslationView.tsx +379 -0
- package/src/components/tab/TranslationsTab.tsx +25 -0
- package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +6 -9
- package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +5 -24
- package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +3 -23
- package/src/configuration/baseDocumentLevelConfig/index.ts +16 -68
- package/src/configuration/baseFieldLevelConfig.ts +15 -50
- package/src/index.ts +29 -43
- package/src/sanity-api/findDocuments.ts +44 -0
- package/src/sanity-api/publishDocuments.ts +49 -0
- package/src/sanity-api/resolveRefs.ts +146 -0
- package/src/serialization/BaseDocumentMerger.ts +138 -0
- package/src/serialization/BaseSerializationConfig.ts +220 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
- package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
- package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
- package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
- package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
- package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
- package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
- package/src/serialization/__tests__/README +2 -0
- package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
- package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
- package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
- package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
- package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
- package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
- package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
- package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
- package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
- package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
- package/src/serialization/__tests__/global.setup.ts +40 -0
- package/src/serialization/__tests__/helpers.ts +132 -0
- package/src/serialization/data.ts +82 -0
- package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
- package/src/serialization/deserialize/helpers.ts +42 -0
- package/src/serialization/helpers.ts +18 -0
- package/src/serialization/index.ts +11 -0
- package/src/serialization/serialize/fieldFilters.ts +124 -0
- package/src/serialization/serialize/index.ts +284 -0
- package/src/serialization/types.ts +41 -0
- package/src/translation/importDocument.ts +4 -5
- package/src/translation/uploadFiles.ts +1 -1
- package/src/types.ts +3 -19
- package/src/utils/batchProcessor.ts +111 -0
- package/src/utils/importUtils.ts +95 -0
- package/src/utils/serialize.ts +25 -5
- package/src/utils/shared.ts +1 -1
- package/src/adapter/index.ts +0 -13
- package/src/components/NewTask.tsx +0 -251
- package/src/components/TaskView.tsx +0 -257
- package/src/components/TranslationContext.tsx +0 -24
- package/src/components/TranslationView.tsx +0 -114
- package/src/components/TranslationsTab.tsx +0 -181
- /package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +0 -0
- /package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getDeserialized } from '../helpers';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import clone from 'just-clone';
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
|
|
7
|
+
const documentLevelArticle = require('../__fixtures__/documentLevelArticle.json');
|
|
8
|
+
const fieldLevelArticle = require('../__fixtures__/fieldLevelArticle.json');
|
|
9
|
+
|
|
10
|
+
export const getNewObject = (): Record<string, any> => {
|
|
11
|
+
const newObject = {
|
|
12
|
+
title: 'A new title',
|
|
13
|
+
nestedArrayField: clone(documentLevelArticle.config.nestedArrayField),
|
|
14
|
+
objectAsField: { title: 'A new nested title' },
|
|
15
|
+
_key: null,
|
|
16
|
+
};
|
|
17
|
+
newObject.nestedArrayField[0].children[0].text = 'New text';
|
|
18
|
+
return newObject;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getNewDocument = (): Record<string, any> => {
|
|
22
|
+
const newDocument = getDeserialized(documentLevelArticle, 'document');
|
|
23
|
+
newDocument.title = 'A new document title';
|
|
24
|
+
newDocument.snippet = 'A new document snippet';
|
|
25
|
+
newDocument.config = getNewObject();
|
|
26
|
+
const newBlockText = newDocument.content[0];
|
|
27
|
+
newBlockText.children[0].text = 'New block text';
|
|
28
|
+
newDocument.content = [newBlockText];
|
|
29
|
+
return newDocument;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getNewFieldLevelObject = (): Record<string, any> => {
|
|
33
|
+
const newObject = {
|
|
34
|
+
title: 'A new title',
|
|
35
|
+
nestedArrayField: clone(fieldLevelArticle.config.en.nestedArrayField),
|
|
36
|
+
objectAsField: { title: 'A new nested title' },
|
|
37
|
+
_key: null,
|
|
38
|
+
};
|
|
39
|
+
newObject.nestedArrayField[0].children[0].text = 'New text';
|
|
40
|
+
return newObject;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const getNewFieldLevelDocument = (): Record<string, any> => {
|
|
44
|
+
const newDocument = getDeserialized(fieldLevelArticle, 'field');
|
|
45
|
+
newDocument.title.en = 'A new document title';
|
|
46
|
+
newDocument.snippet.en = 'A new document snippet';
|
|
47
|
+
newDocument.config.en = getNewFieldLevelObject();
|
|
48
|
+
const newBlockText = newDocument.content.en[0];
|
|
49
|
+
newBlockText.children[0].text = 'New block text';
|
|
50
|
+
newDocument.content.en = [newBlockText];
|
|
51
|
+
return newDocument;
|
|
52
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Global test of working doc-level functionality and snapshot match 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"content": "<html><head><meta name="_id" content="drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a"><meta name="_type" content="documentLevelArticle"><meta name="_rev" content="fxcnqj-uew-gr7-v6r-z965tfm69"><meta name="version" content="3"></head><body><div class="documentLevelArticle" id="drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a" data-type="object"><span class="title">My Document-Level Article</span><span class="snippet">This is text in my text field.</span><div class="tags" data-type="array"><span>tag 1</span><span>tag 2</span><span>tag 3</span></div><div class="content" data-type="array"><p id="a4d177e92666">This is block text with <span class="unknown__pt__mark__linkField">a website link</span> and <span class="unknown__pt__mark__linkField">a post reference</span><span class="unknown__pt__mark__linkField"> at the top level.</span></p></div></div></body></html>",
|
|
6
|
+
"name": "drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a",
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
exports[`Test of working deserialization and snapshot match 1`] = `
|
|
11
|
+
{
|
|
12
|
+
"_id": "drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a",
|
|
13
|
+
"_rev": "fxcnqj-uew-gr7-v6r-z965tfm69",
|
|
14
|
+
"_type": "documentLevelArticle",
|
|
15
|
+
"content": [
|
|
16
|
+
{
|
|
17
|
+
"_key": "a4d177e92666",
|
|
18
|
+
"_type": "block",
|
|
19
|
+
"children": [
|
|
20
|
+
{
|
|
21
|
+
"_key": "randomKey-10",
|
|
22
|
+
"_type": "span",
|
|
23
|
+
"marks": [],
|
|
24
|
+
"text": "This is block text with a website link and a post reference at the top level.",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
"markDefs": [],
|
|
28
|
+
"style": "normal",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
"snippet": "This is text in my text field.",
|
|
32
|
+
"tags": [
|
|
33
|
+
"tag 1",
|
|
34
|
+
"tag 2",
|
|
35
|
+
"tag 3",
|
|
36
|
+
],
|
|
37
|
+
"title": "My Document-Level Article",
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Global test of working doc-level functionality and snapshot match 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"content": "<html><head><meta name="_id" content="drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a"><meta name="_type" content="documentLevelArticle"><meta name="_rev" content="fxcnqj-uew-gr7-v6r-z965tfm69"><meta name="version" content="3"></head><body><div class="documentLevelArticle" id="drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a" data-type="object"><span class="title">My Document-Level Article</span><span class="snippet">This is text in my text field.</span><div class="tags" data-type="array"><span>tag 1</span><span>tag 2</span><span>tag 3</span></div><div class="config" data-level="field"><div class="objectField" data-type="object"><span class="title">This is an object nested in a document</span><div class="objectAsField" data-level="field"><div class="childObjectField" data-type="object"><span class="title">This is one level deeper</span><div class="content" data-type="array"><p id="1aa228ac848b">This is a block text 2 levels deep</p></div></div></div><div class="nestedArrayField" data-type="array"><p id="4a58adc7c507">This is block text 1 level deep</p></div></div></div><div class="content" data-type="array"><p id="a4d177e92666">This is block text at the top level.</p><div class="objectField" id="c0313627775e" data-type="object"><span class="title">This is an object in top-level block text.</span><div class="objectAsField" data-level="field"><div class="childObjectField" data-type="object"><span class="title">This is a nested object in an object in top-level block text.</span><div class="content" data-type="array"><p id="4f24f6fbfae7">This is block text in a nested object in an object in top-level block text.</p></div></div></div></div><h1 id="9e2ab13c6d63">This is h1 text</h1><h2 id="297142fbaf21">This is h2 text</h2><ul id="76e648fc3845"><li id="76e648fc3845" data-level="1">Bullet 1</li></ul><ul id="d090cd8b27d2"><li id="d090cd8b27d2" data-level="2">nested bullet a</li></ul><ul id="1cdffa5d50f5"><li id="1cdffa5d50f5" data-level="1"><h2 id="1cdffa5d50f5">Styled bullet 2</h2></li></ul><ol id="da29f5063059"><li id="da29f5063059" data-level="1"><h3 id="da29f5063059">Number 1</h3></li></ol></div></div></body></html>",
|
|
6
|
+
"name": "drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a",
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Global test of working field-level functionality and snapshot match 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"content": "<html><head><meta name="_id" content="drafts.2947533e-1ea5-4116-955b-339608d3445d"><meta name="_type" content="fieldLevelArticle"><meta name="_rev" content="l58oha-n06-1s4-f6i-74g6cuakl"><meta name="version" content="3"></head><body><div class="fieldLevelArticle" id="drafts.2947533e-1ea5-4116-955b-339608d3445d" data-type="object"><div class="title" data-level="field"><div class="" data-type="object"><span class="en">My Field-Level Article</span></div></div><div class="snippet" data-level="field"><div class="" data-type="object"><span class="en">This is text in my text field</span></div></div><div class="tags" data-level="field"><div class="" data-type="object"><div class="en" data-type="array"><span>tag 1</span><span>tag 2</span><span>tag 3</span></div></div></div><div class="config" data-level="field"><div class="" data-type="object"><div class="en" data-level="field"><div class="objectField" data-type="object"><span class="title">This is an object nested in a document</span><div class="objectAsField" data-level="field"><div class="childObjectField" data-type="object"><span class="title">This is one level deeper</span><div class="content" data-type="array"><p id="869168fe3a2a">This is block text 2 levels deep</p></div></div></div><div class="nestedArrayField" data-type="array"><p id="f49b4d7e3e51">This is block text 1 level deep</p></div></div></div></div></div><div class="content" data-level="field"><div class="" data-type="object"><div class="en" data-type="array"><p id="e2a39d768ff8">This is block text at the top level.</p><div class="objectField" id="271b0c6ee984" data-type="object"><span class="title">This is an object in top-level block text.</span><div class="objectAsField" data-level="field"><div class="childObjectField" data-type="object"><span class="title">This is a nested object in an object in top-level block text.</span><div class="content" data-type="array"><p id="4f24f6fbfae7">This is block text in a nested object in an object in top-level block text.</p></div></div></div></div></div></div></div></div></body></html>",
|
|
6
|
+
"name": "drafts.2947533e-1ea5-4116-955b-339608d3445d",
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { PortableTextBlock } from 'sanity';
|
|
2
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
BaseDocumentSerializer,
|
|
5
|
+
customSerializers,
|
|
6
|
+
defaultStopTypes,
|
|
7
|
+
} from '../../index';
|
|
8
|
+
import {
|
|
9
|
+
addedCustomSerializers,
|
|
10
|
+
createCustomInnerHTML,
|
|
11
|
+
getSerialized,
|
|
12
|
+
getValidFields,
|
|
13
|
+
} from '../helpers';
|
|
14
|
+
import {
|
|
15
|
+
annotationAndInlineBlocks,
|
|
16
|
+
documentLevelArticle,
|
|
17
|
+
findByClass,
|
|
18
|
+
getHTMLNode,
|
|
19
|
+
inlineDocumentLevelArticle,
|
|
20
|
+
inlineSchema,
|
|
21
|
+
schema,
|
|
22
|
+
} from './utils';
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* METADATA PRESENCE
|
|
26
|
+
*/
|
|
27
|
+
describe('Has all required metadata', () => {
|
|
28
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
29
|
+
const docTree = getHTMLNode(serialized);
|
|
30
|
+
test('Contains metadata field containing document id', () => {
|
|
31
|
+
const idMetaTag = Array.from(docTree.head.children).find(
|
|
32
|
+
(metaTag) => metaTag.getAttribute('name') === '_id'
|
|
33
|
+
);
|
|
34
|
+
const id = idMetaTag?.getAttribute('content');
|
|
35
|
+
expect(id).toEqual(documentLevelArticle._id);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('Contains metadata field containing document revision', () => {
|
|
39
|
+
const revMetaTag = Array.from(docTree.head.children).find(
|
|
40
|
+
(metaTag) => metaTag.getAttribute('name') === '_rev'
|
|
41
|
+
);
|
|
42
|
+
const rev = revMetaTag?.getAttribute('content');
|
|
43
|
+
expect(rev).toEqual(documentLevelArticle._rev);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Contains metadata field containing document type', () => {
|
|
47
|
+
const typeMetaTag = Array.from(docTree.head.children).find(
|
|
48
|
+
(metaTag) => metaTag.getAttribute('name') === '_type'
|
|
49
|
+
);
|
|
50
|
+
const type = typeMetaTag?.getAttribute('content');
|
|
51
|
+
expect(type).toEqual(documentLevelArticle._type);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('Contains metadata field containing version', () => {
|
|
55
|
+
const typeMetaTag = Array.from(docTree.head.children).find(
|
|
56
|
+
(metaTag) => metaTag.getAttribute('name') === 'version'
|
|
57
|
+
);
|
|
58
|
+
const version = typeMetaTag?.getAttribute('content');
|
|
59
|
+
expect(version).toEqual('3');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* CUSTOM SETTINGS
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
test('Custom serialization should manifest at all levels', () => {
|
|
68
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
69
|
+
const serialized = serializer.serializeDocument(
|
|
70
|
+
documentLevelArticle,
|
|
71
|
+
'document',
|
|
72
|
+
'en',
|
|
73
|
+
defaultStopTypes,
|
|
74
|
+
addedCustomSerializers
|
|
75
|
+
);
|
|
76
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
77
|
+
|
|
78
|
+
const topLevelCustomSerialized = findByClass(docTree.children, 'config');
|
|
79
|
+
const requiredTopLevelTitle = documentLevelArticle.config.title;
|
|
80
|
+
expect(topLevelCustomSerialized?.innerHTML).toContain(
|
|
81
|
+
createCustomInnerHTML(requiredTopLevelTitle)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
85
|
+
const nestedSerialized = findByClass(arrayField!.children, 'objectField');
|
|
86
|
+
const requiredNestedTitle = documentLevelArticle.content.find(
|
|
87
|
+
(b: Record<string, any>) => b._type === 'objectField'
|
|
88
|
+
).title;
|
|
89
|
+
expect(nestedSerialized?.innerHTML).toContain(
|
|
90
|
+
createCustomInnerHTML(requiredNestedTitle)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('Fields marked "localize: false" should not be serialized', () => {
|
|
95
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
96
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
97
|
+
//"meta" is localize: false field
|
|
98
|
+
const meta = findByClass(docTree.children, 'meta');
|
|
99
|
+
expect(documentLevelArticle.meta).toBeDefined();
|
|
100
|
+
expect(meta).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('Expect default stop types to be absent', () => {
|
|
104
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
105
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
106
|
+
//"hidden" is boolean field
|
|
107
|
+
const hidden = findByClass(docTree.children, 'hidden');
|
|
108
|
+
expect(documentLevelArticle.hidden).toBeDefined();
|
|
109
|
+
expect(hidden).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('Expect custom stop types to be absent at all levels', () => {
|
|
113
|
+
const customStopTypes = [...defaultStopTypes, 'objectField'];
|
|
114
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
115
|
+
const serialized = serializer.serializeDocument(
|
|
116
|
+
documentLevelArticle,
|
|
117
|
+
'document',
|
|
118
|
+
'en',
|
|
119
|
+
customStopTypes,
|
|
120
|
+
customSerializers
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
124
|
+
const config = findByClass(docTree.children, 'config');
|
|
125
|
+
expect(documentLevelArticle.config).toBeDefined();
|
|
126
|
+
expect(config).toBeUndefined();
|
|
127
|
+
|
|
128
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
129
|
+
const nestedSerialized = findByClass(arrayField!.children, 'objectField');
|
|
130
|
+
const nestedObjField = documentLevelArticle.content.find(
|
|
131
|
+
(b: Record<string, any>) => b._type === 'objectField'
|
|
132
|
+
);
|
|
133
|
+
expect(nestedObjField).toBeDefined();
|
|
134
|
+
expect(nestedSerialized).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* ANNOTATION AND INLINE BLOCK CONTENT
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
test('Unhandled inline objects and annotations should not hinder translation flows', () => {
|
|
142
|
+
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
143
|
+
|
|
144
|
+
const inlineDocument = {
|
|
145
|
+
...documentLevelArticle,
|
|
146
|
+
...annotationAndInlineBlocks,
|
|
147
|
+
};
|
|
148
|
+
const serialized = getSerialized(inlineDocument, 'document');
|
|
149
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
150
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
151
|
+
|
|
152
|
+
//expect annotated object to have underlying text
|
|
153
|
+
const blockWithAnnotation = Array.from(arrayField!.children).find(
|
|
154
|
+
(node) => node.id === '0e55995095df'
|
|
155
|
+
);
|
|
156
|
+
const unhandledAnnotation = findByClass(
|
|
157
|
+
blockWithAnnotation!.children,
|
|
158
|
+
'unknown__pt__mark__annotation'
|
|
159
|
+
);
|
|
160
|
+
expect(unhandledAnnotation?.innerHTML).toContain('text');
|
|
161
|
+
|
|
162
|
+
//expect unknown inline object to be present but empty
|
|
163
|
+
//(this allows it to be merged back safely, but not sent to translation)
|
|
164
|
+
const inlineObject = findByClass(arrayField!.children, 'childObjectField');
|
|
165
|
+
expect(inlineObject?.innerHTML.length).toEqual(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('Handled inline objects should be accurately represented per serializer', () => {
|
|
169
|
+
const inlineDocument = {
|
|
170
|
+
...documentLevelArticle,
|
|
171
|
+
...annotationAndInlineBlocks,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
175
|
+
const serialized = serializer.serializeDocument(
|
|
176
|
+
inlineDocument,
|
|
177
|
+
'document',
|
|
178
|
+
'en',
|
|
179
|
+
defaultStopTypes,
|
|
180
|
+
addedCustomSerializers
|
|
181
|
+
);
|
|
182
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
183
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
184
|
+
let inlineObject: Element | null = null;
|
|
185
|
+
let inlineObjectBlock: Record<string, any> | null = null;
|
|
186
|
+
|
|
187
|
+
Array.from(arrayField!.children).forEach((block: any) => {
|
|
188
|
+
if (!inlineObject) {
|
|
189
|
+
inlineObject =
|
|
190
|
+
findByClass(block.children, 'childObjectField') ?? inlineObject;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
inlineDocument.content.forEach((block: Record<string, any>) => {
|
|
195
|
+
if (block.children) {
|
|
196
|
+
block.children.forEach((span: Record<string, any>) => {
|
|
197
|
+
if (span._type === 'childObjectField') {
|
|
198
|
+
inlineObjectBlock = span;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(inlineObject!.innerHTML).toContain(
|
|
205
|
+
createCustomInnerHTML(inlineObjectBlock!.title)
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('Handled annotations should be accurately represented per serializer', () => {
|
|
210
|
+
const inlineDocument = {
|
|
211
|
+
...documentLevelArticle,
|
|
212
|
+
...annotationAndInlineBlocks,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
216
|
+
const serialized = serializer.serializeDocument(
|
|
217
|
+
inlineDocument,
|
|
218
|
+
'document',
|
|
219
|
+
'en',
|
|
220
|
+
defaultStopTypes,
|
|
221
|
+
addedCustomSerializers
|
|
222
|
+
);
|
|
223
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
224
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
225
|
+
let annotation: Element | null = null;
|
|
226
|
+
let annotationBlock: Record<string, any> | null = null;
|
|
227
|
+
|
|
228
|
+
Array.from(arrayField!.children).forEach((block: any) => {
|
|
229
|
+
if (!annotation) {
|
|
230
|
+
annotation = findByClass(block.children, 'annotation') ?? annotation;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
inlineDocument.content.forEach((block: PortableTextBlock) => {
|
|
235
|
+
if (block.children && Array.isArray(block.children)) {
|
|
236
|
+
block.children.forEach((span: Record<string, any>) => {
|
|
237
|
+
if (span.marks && span.marks.length) {
|
|
238
|
+
annotationBlock = span;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(annotation!.innerHTML).toEqual(annotationBlock!.text);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* STYLE TAGS
|
|
249
|
+
*/
|
|
250
|
+
test('Serialized content should preserve style tags from Portable Text', () => {
|
|
251
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
252
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
253
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
254
|
+
const blockH1 = documentLevelArticle.content.find(
|
|
255
|
+
(block: PortableTextBlock) => block.style === 'h1'
|
|
256
|
+
);
|
|
257
|
+
const serializedH1 = arrayField?.querySelector('h1');
|
|
258
|
+
const blockH2 = documentLevelArticle.content.find(
|
|
259
|
+
(block: PortableTextBlock) => block.style === 'h2'
|
|
260
|
+
);
|
|
261
|
+
const serializedH2 = arrayField?.querySelector('h2');
|
|
262
|
+
expect(serializedH1?.innerHTML).toEqual(blockH1.children[0].text);
|
|
263
|
+
expect(serializedH2?.innerHTML).toEqual(blockH2.children[0].text);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
/*
|
|
267
|
+
* V2 functionality -- be able to operate without a strict schema
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
test('Content with anonymous inline objects serializes all fields, at any depth', () => {
|
|
271
|
+
const serialized = BaseDocumentSerializer(inlineSchema).serializeDocument(
|
|
272
|
+
inlineDocumentLevelArticle,
|
|
273
|
+
'document'
|
|
274
|
+
);
|
|
275
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
276
|
+
const tabs = findByClass(docTree.children, 'tabs')!.children[0];
|
|
277
|
+
const config = findByClass(tabs!.children, 'config')!.children[0];
|
|
278
|
+
const fieldNames = getValidFields(inlineDocumentLevelArticle.tabs.config);
|
|
279
|
+
const foundFieldNames = Array.from(config!.children).map(
|
|
280
|
+
(child) => child.className
|
|
281
|
+
);
|
|
282
|
+
expect(foundFieldNames.sort()).toEqual(fieldNames.sort());
|
|
283
|
+
const nestedObjHTML = findByClass(config!.children, 'objectAsField')!
|
|
284
|
+
.children[0];
|
|
285
|
+
const nestedObj = inlineDocumentLevelArticle.tabs.config.objectAsField;
|
|
286
|
+
const nestedFieldNames = Array.from(nestedObjHTML!.children).map(
|
|
287
|
+
(child) => child.className
|
|
288
|
+
);
|
|
289
|
+
expect(nestedFieldNames.sort()).toEqual(getValidFields(nestedObj).sort());
|
|
290
|
+
|
|
291
|
+
const content = findByClass(tabs!.children, 'content')!;
|
|
292
|
+
const keysHTML = Array.from(content.children).map((child) => child.id);
|
|
293
|
+
const keysJSON = inlineDocumentLevelArticle.tabs.content.map(
|
|
294
|
+
(child: any) => child._key
|
|
295
|
+
);
|
|
296
|
+
expect(keysHTML.sort()).toEqual(keysJSON.sort());
|
|
297
|
+
|
|
298
|
+
const objectInArrayHTML = findByClass(content.children, 'objectField');
|
|
299
|
+
const objectInArrayHTMLFieldNames = Array.from(
|
|
300
|
+
objectInArrayHTML!.children
|
|
301
|
+
).map((child) => child.className);
|
|
302
|
+
const objectInArray = inlineDocumentLevelArticle.tabs.content.find(
|
|
303
|
+
(obj: any) => obj._type === 'objectField'
|
|
304
|
+
);
|
|
305
|
+
expect(objectInArrayHTMLFieldNames.sort()).toEqual(
|
|
306
|
+
getValidFields(objectInArray).sort()
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
/*
|
|
311
|
+
* LIST ITEMS
|
|
312
|
+
*/
|
|
313
|
+
test('Serialized content should preserve list style and depth from Portable text', () => {
|
|
314
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
315
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
316
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
317
|
+
const listItem = documentLevelArticle.content.find(
|
|
318
|
+
(block: PortableTextBlock) =>
|
|
319
|
+
block.listItem === 'bullet' && block.style === 'h2'
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const serializedListItem = arrayField?.querySelectorAll('li')[2];
|
|
323
|
+
const nestedListItem = documentLevelArticle.content.find(
|
|
324
|
+
(block: PortableTextBlock) =>
|
|
325
|
+
block.listItem === 'bullet' && block.level === 2
|
|
326
|
+
);
|
|
327
|
+
const serializedNestedListItem = arrayField?.querySelectorAll('li')[1];
|
|
328
|
+
//include quote style for completeness
|
|
329
|
+
expect(serializedListItem?.innerHTML).toContain(listItem.children[0].text);
|
|
330
|
+
expect(serializedListItem?.innerHTML).toContain('h2');
|
|
331
|
+
|
|
332
|
+
expect(serializedNestedListItem?.innerHTML).toEqual(
|
|
333
|
+
nestedListItem.children[0].text
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test('Values in a field are not repeated (indicating serializers are stateless)', () => {
|
|
338
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
339
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
340
|
+
const HTMLList = findByClass(docTree.children, 'tags');
|
|
341
|
+
const tags = documentLevelArticle.tags;
|
|
342
|
+
expect(HTMLList?.innerHTML).toContain(tags[0]);
|
|
343
|
+
expect(HTMLList?.innerHTML).toContain(tags[1]);
|
|
344
|
+
expect(HTMLList?.innerHTML).toContain(tags[2]);
|
|
345
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PortableTextBlock, SanityDocument } from 'sanity';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
getSerialized,
|
|
5
|
+
getValidFields,
|
|
6
|
+
toPlainText,
|
|
7
|
+
getDeserialized,
|
|
8
|
+
} from '../helpers';
|
|
9
|
+
import { docWithInlineMarks, findByClass, getHTMLNode, schema } from './utils';
|
|
10
|
+
import { attachGTData, BaseDocumentSerializer, customSerializers } from '../..';
|
|
11
|
+
import { TranslationLevel } from '../../types';
|
|
12
|
+
import { defaultStopTypes } from '../..';
|
|
13
|
+
import merge from 'lodash.merge';
|
|
14
|
+
import { PortableTextHtmlComponents } from '@portabletext/to-html';
|
|
15
|
+
|
|
16
|
+
function serialize(document: SanityDocument, level: TranslationLevel) {
|
|
17
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
18
|
+
return serializer.serializeDocument(
|
|
19
|
+
document,
|
|
20
|
+
level,
|
|
21
|
+
'en',
|
|
22
|
+
defaultStopTypes,
|
|
23
|
+
merge(customSerializers, {
|
|
24
|
+
marks: {
|
|
25
|
+
link: ({ value, children }) =>
|
|
26
|
+
attachGTData(`<a>${children}</a>`, value, 'markDef'),
|
|
27
|
+
inlineMath: ({ value, children }) =>
|
|
28
|
+
attachGTData(`<span>${children}</span>`, value, 'markDef'),
|
|
29
|
+
},
|
|
30
|
+
} satisfies Partial<PortableTextHtmlComponents>)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const serialized = serialize(docWithInlineMarks, 'document');
|
|
35
|
+
const deserialized = getDeserialized(docWithInlineMarks, 'document');
|
|
36
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
37
|
+
|
|
38
|
+
test('Global test of working doc-level functionality and snapshot match', () => {
|
|
39
|
+
expect(serialized).toMatchSnapshot();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('Test of working deserialization and snapshot match', () => {
|
|
43
|
+
expect(deserialized).toMatchSnapshot();
|
|
44
|
+
});
|
|
45
|
+
/*
|
|
46
|
+
* Top-level plain text
|
|
47
|
+
*/
|
|
48
|
+
test('String and text types get serialized correctly at top-level', () => {
|
|
49
|
+
const HTMLString = findByClass(docTree.children, 'title');
|
|
50
|
+
const HTMLText = findByClass(docTree.children, 'snippet');
|
|
51
|
+
expect(HTMLString?.innerHTML).toEqual(docWithInlineMarks.title);
|
|
52
|
+
expect(HTMLText?.innerHTML).toEqual(docWithInlineMarks.snippet);
|
|
53
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { PortableTextBlock } from 'sanity';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { getSerialized, getValidFields, toPlainText } from '../helpers';
|
|
4
|
+
import { documentLevelArticle, findByClass, getHTMLNode } from './utils';
|
|
5
|
+
|
|
6
|
+
const serialized = getSerialized(documentLevelArticle, 'document');
|
|
7
|
+
const docTree = getHTMLNode(serialized).body.children[0];
|
|
8
|
+
|
|
9
|
+
test('Global test of working doc-level functionality and snapshot match', () => {
|
|
10
|
+
expect(serialized).toMatchSnapshot();
|
|
11
|
+
});
|
|
12
|
+
/*
|
|
13
|
+
* Top-level plain text
|
|
14
|
+
*/
|
|
15
|
+
test('String and text types get serialized correctly at top-level', () => {
|
|
16
|
+
const HTMLString = findByClass(docTree.children, 'title');
|
|
17
|
+
const HTMLText = findByClass(docTree.children, 'snippet');
|
|
18
|
+
expect(HTMLString?.innerHTML).toEqual(documentLevelArticle.title);
|
|
19
|
+
expect(HTMLText?.innerHTML).toEqual(documentLevelArticle.snippet);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Presence and accuracy of fields
|
|
24
|
+
*/
|
|
25
|
+
describe('Presence and accuracy of fields in "vanilla" deserialization -- objects', () => {
|
|
26
|
+
//parent node is always div with classname of field with a nested div
|
|
27
|
+
//that has classname of obj type
|
|
28
|
+
const configObj = findByClass(docTree.children, 'config');
|
|
29
|
+
const objectField = configObj!.children[0];
|
|
30
|
+
|
|
31
|
+
test('Top-level nested objects contain all serializable fields -- document level', () => {
|
|
32
|
+
const fieldNames = getValidFields(documentLevelArticle.config);
|
|
33
|
+
const foundFieldNames = Array.from(objectField!.children).map(
|
|
34
|
+
(child) => child.className
|
|
35
|
+
);
|
|
36
|
+
expect(foundFieldNames.sort()).toEqual(fieldNames.sort());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('Nested object in object contains all serializable fields -- document level', () => {
|
|
40
|
+
const nestedObject = findByClass(objectField!.children, 'objectAsField')!
|
|
41
|
+
.children[0];
|
|
42
|
+
const fieldNames = getValidFields(
|
|
43
|
+
documentLevelArticle.config.objectAsField
|
|
44
|
+
);
|
|
45
|
+
const foundFieldNames = Array.from(nestedObject!.children).map(
|
|
46
|
+
(child) => child.className
|
|
47
|
+
);
|
|
48
|
+
expect(foundFieldNames.sort()).toEqual(fieldNames.sort());
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('Nested object contains accurate values -- document level', () => {
|
|
52
|
+
const title = documentLevelArticle.config.title;
|
|
53
|
+
const blockText = toPlainText(documentLevelArticle.config.nestedArrayField);
|
|
54
|
+
|
|
55
|
+
expect(objectField?.innerHTML).toContain(title);
|
|
56
|
+
expect(objectField?.innerHTML).toContain(blockText);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('Nested object in an object contains accurate values -- document level', () => {
|
|
60
|
+
const nestedObject = findByClass(objectField!.children, 'objectAsField')!
|
|
61
|
+
.children[0];
|
|
62
|
+
const title = documentLevelArticle.config.objectAsField.title;
|
|
63
|
+
const blockText = toPlainText(
|
|
64
|
+
documentLevelArticle.config.objectAsField.content
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(nestedObject.innerHTML).toContain(title);
|
|
68
|
+
expect(nestedObject.innerHTML).toContain(blockText);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('Presence and accuracy of fields in vanilla deserialization -- arrays', () => {
|
|
73
|
+
const arrayField = findByClass(docTree.children, 'content');
|
|
74
|
+
|
|
75
|
+
test('Array contains all serializable blocks with keys, in order -- document level', () => {
|
|
76
|
+
const origKeys = documentLevelArticle.content.map(
|
|
77
|
+
(block: PortableTextBlock) => block._key
|
|
78
|
+
);
|
|
79
|
+
const serializedKeys = Array.from(arrayField!.children).map(
|
|
80
|
+
(block) => block.id
|
|
81
|
+
);
|
|
82
|
+
expect(serializedKeys).toEqual(origKeys);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('Array contains top-level block text -- document level', () => {
|
|
86
|
+
const blockText = toPlainText(documentLevelArticle.content).trim();
|
|
87
|
+
const blockStrings = blockText.split('\n\n');
|
|
88
|
+
blockStrings.forEach((substring: string) =>
|
|
89
|
+
expect(arrayField?.innerHTML).toContain(substring)
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Object in array contains all serializable fields -- document level', () => {
|
|
94
|
+
const objectInArray = findByClass(arrayField!.children, 'objectField');
|
|
95
|
+
const fieldNames = getValidFields(
|
|
96
|
+
documentLevelArticle.content.find(
|
|
97
|
+
(block: Record<string, any>) => block._type === 'objectField'
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
const foundFieldNames = Array.from(objectInArray!.children).map(
|
|
101
|
+
(child) => child.className
|
|
102
|
+
);
|
|
103
|
+
expect(foundFieldNames.sort()).toEqual(fieldNames.sort());
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('Object in array contains accurate values in nested object -- document level', () => {
|
|
107
|
+
const objectInArray = findByClass(arrayField!.children, 'objectField');
|
|
108
|
+
const nestedObject = findByClass(objectInArray!.children, 'objectAsField');
|
|
109
|
+
const title = documentLevelArticle.content.find(
|
|
110
|
+
(block: Record<string, any>) => block._type === 'objectField'
|
|
111
|
+
).objectAsField.title;
|
|
112
|
+
const blockText = toPlainText(
|
|
113
|
+
documentLevelArticle.content.find(
|
|
114
|
+
(block: Record<string, any>) => block._type === 'objectField'
|
|
115
|
+
).objectAsField.content
|
|
116
|
+
).trim();
|
|
117
|
+
expect(nestedObject?.innerHTML).toContain(title);
|
|
118
|
+
expect(nestedObject?.innerHTML).toContain(blockText);
|
|
119
|
+
});
|
|
120
|
+
});
|