mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: crates (#4258)
* chore: rename flowy-folder2 to flowy-folder * chore: rename flowy-document2 to flowy-document * chore: fix test * chore: move lib-infra crate * chore: remove shared-lib * chore: fix clippy
This commit is contained in:
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><ul><li>Highlight<p>You can also</p><ul><li>nest</li></ul></li></ul>
|
@ -0,0 +1,6 @@
|
||||
<meta charset="UTF-8"><aside>🥰
|
||||
Like AppFlowy? Follow us:
|
||||
<a href="https://github.com/AppFlowy-IO/AppFlowy">GitHub</a>
|
||||
<a href="https://twitter.com/appflowy">Twitter</a>: @appflowy
|
||||
<a href="https://blog-appflowy.ghost.io/">Newsletter</a>
|
||||
</aside>
|
@ -0,0 +1,5 @@
|
||||
<meta charset="UTF-8"><pre><code class="language-rust">// This is the main function.
|
||||
fn main() {
|
||||
// Print text to the console.
|
||||
println!("Hello World!");
|
||||
}</code></pre>
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><hr />
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><h1>Heading1</h1><h2>Heading2</h2><h3>Heading3</h3>
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" alt=AppFlowy-Image />
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><p>E = MC^2</p>
|
@ -0,0 +1,34 @@
|
||||
<meta charset='utf-8'><h1>The Notion Document</h1>
|
||||
<h1>Heading-1</h1>
|
||||
<h2>Heading - 2</h2>
|
||||
<h3>Heading - 3</h3>
|
||||
<p>This is a paragraph</p>
|
||||
<p>paragraph’s child</p>
|
||||
<ul><li>This is a bulleted list - 1<ul><li>This is a bulleted list - 1 - 1</li></ul></li><li>This is a bulleted list - 2</li></ul>
|
||||
<p>This is a paragraph</p>
|
||||
<ul><li>[ ] This is a todo - 1<ul><li>[ ] This is a paragraph - 1-1</li></ul></li></ul>
|
||||
<ol><li>This is a numbered list -1</li></ol>
|
||||
<p>This is a paragraph</p>
|
||||
<ul><li><p>This is a toggle list</p><p>This is a toggle child</p></li>
|
||||
</ul>
|
||||
<blockquote><p>This is a quote</p><p>This is a quote child</p></blockquote>
|
||||
<p>This is a paragraph</p>
|
||||
<hr>
|
||||
<pre><code class="language-jsx">// This is the main function.
|
||||
fn main() {
|
||||
// Print text to the console.
|
||||
**println**!("Hello World!");
|
||||
}</code></pre>
|
||||
<p>This is a paragraph</p>
|
||||
<p><aside>
|
||||
💡 callout</p>
|
||||
<p></aside></p>
|
||||
<p>This is a paragraph font-color bg-color <strong>bold</strong> <em>italic underline <s>strike-through</s> <code>inline-code</code> $inline-formula$ <a href="https://www.notion.so/The-Notion-Document-d4236da306b84f6199e4091705042d78?pvs=21">link</a></em></p>
|
||||
<p>$$
|
||||
|x| = \begin{cases}
|
||||
x, &\quad x \geq 0 \\
|
||||
-x, &\quad x < 0
|
||||
\end{cases}
|
||||
$$</p>
|
||||
<p>End</p>
|
||||
<!-- notionvc: 0b0229d7-b98a-4e36-8a64-f944de21ef0e -->
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><ol><li>Highlight<p>You can also</p><ol><li>nest</li></ol></li></ol>
|
@ -0,0 +1,6 @@
|
||||
<meta charset="UTF-8"><p>
|
||||
Like AppFlowy? Follow us:
|
||||
<a href="https://github.com/AppFlowy-IO/AppFlowy">GitHub</a>
|
||||
<a href="https://twitter.com/appflowy">Twitter</a>: @appflowy
|
||||
<a href="https://blog-appflowy.ghost.io/">Newsletter</a>
|
||||
</p><p>Click <code>?</code> at the bottom right for help and support.</p><p><span style="background-color: #4dffeb3b;">Highlight </span>any text, and use the editing menu to <span style="font-style: italic;">style</span> <span style="font-weight: bold;">your</span> <span style="text-decoration: underline;">writing</span> <code>however</code><span style="color: #4dffeb3b;"> you </span><span style="text-decoration: line-through;">like.</span><span style="font-family: fantasy;">1+1=2</span></p>
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><blockquote><p>This is a quote</p><p>This is a paragraph</p></blockquote>
|
@ -0,0 +1 @@
|
||||
<span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: -webkit-standard; font-size: medium; font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; display: inline !important; float: none;">This is a paragraph</span>
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><ul><li role="checkbox" aria-checked="true">Highlight<p>You can also</p><ul><li role="checkbox" aria-checked="false">nest</li></ul></li></ul>
|
@ -0,0 +1 @@
|
||||
<meta charset="UTF-8"><details><summary>Click <code>?</code> at the bottom right for help and support.</summary><p>This is a paragraph</p><details><summary>This is a toggle list</summary></details></details>
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Highlight"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "You can also"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "nest"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "callout",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "\nLike AppFlowy? Follow us:\n" },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://github.com/AppFlowy-IO/AppFlowy"
|
||||
},
|
||||
"insert": "GitHub"
|
||||
},
|
||||
{ "insert": "\n" },
|
||||
{
|
||||
"attributes": { "href": "https://twitter.com/appflowy" },
|
||||
"insert": "Twitter"
|
||||
},
|
||||
{ "insert": ": @appflowy\n" },
|
||||
{
|
||||
"attributes": { "href": "https://blog-appflowy.ghost.io/" },
|
||||
"insert": "Newsletter"
|
||||
},
|
||||
{ "insert": "\n" }
|
||||
],
|
||||
"icon": "🥰"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
14
frontend/rust-lib/flowy-document/tests/assets/json/code.json
Normal file
14
frontend/rust-lib/flowy-document/tests/assets/json/code.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [{
|
||||
"type": "code",
|
||||
"data": {
|
||||
"language": "rust",
|
||||
"delta": [
|
||||
{
|
||||
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n println!(\"Hello World!\");\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "divider",
|
||||
"data": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,332 @@
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "The Notion Document"
|
||||
}
|
||||
],
|
||||
"level": 1,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "Heading-1"
|
||||
}
|
||||
],
|
||||
"level": 1,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "Heading - 2"
|
||||
}
|
||||
],
|
||||
"level": 2,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "Heading - 3"
|
||||
}
|
||||
],
|
||||
"level": 3,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "Heading - 4"
|
||||
}
|
||||
],
|
||||
"level": 3,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "paragraph’s child"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a bulleted list - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "bulleted_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a bulleted list - 1 - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "bulleted_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a bulleted list - 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "bulleted_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAA1ElEQVR4Ae3bMQ4BURSFYY2xBuwQ7BIkTGxFRj9Oo9RdkXn5TvL3L19u+2ZmZmZmZhVbpH26pFcaJ9IrndMudb/CWadHGiden1bll9MIzqd79SUd0thY20qga4NA50qgoUGgoRJo/NL/V/N+QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEyFeEZyXQpUGgUyXQrkGgTSVQl/qGcG5pnkq3Sn0jOMv0k3Vpm05pmNjfsGPalFyOmZmZmdkbSS9cKbtzhxMAAAAASUVORK5CYII="
|
||||
},
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a todo - 1"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"checked": false,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "todo_list"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAA1ElEQVR4Ae3bMQ4BURSFYY2xBuwQ7BIkTGxFRj9Oo9RdkXn5TvL3L19u+2ZmZmZmZhVbpH26pFcaJ9IrndMudb/CWadHGiden1bll9MIzqd79SUd0thY20qga4NA50qgoUGgoRJo/NL/V/N+QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEyFeEZyXQpUGgUyXQrkGgTSVQl/qGcG5pnkq3Sn0jOMv0k3Vpm05pmNjfsGPalFyOmZmZmdkbSS9cKbtzhxMAAAAASUVORK5CYII="
|
||||
},
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a todo - 1-1"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"checked": false,
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "todo_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a numbered list -1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "numbered_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a numbered list -2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "numbered_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a numbered list-1-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "numbered_list"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": {
|
||||
"font_color": "#000000"
|
||||
},
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
],
|
||||
"text_direction": "ltr"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {},
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {},
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"data": {},
|
||||
"type": "page"
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 1,
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Heading1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 2,
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Heading2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 3,
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Heading3"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "image",
|
||||
"data": {
|
||||
"url": "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
|
||||
"width": 272,
|
||||
"height": 92,
|
||||
"align": "center"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "delta": [{ "insert": "Welcome to AppFlowy!" }], "level": 1 }
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "delta": [{ "insert": "Here are the basics" }], "level": 2 }
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "delta": [{ "insert": "Here is H3" }], "level": 3 }
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Click anywhere and just start typing." }],
|
||||
"checked": false
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "Enter" },
|
||||
{ "insert": " to create a new line." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": { "bg_color": "0x4dffeb3b" },
|
||||
"insert": "Highlight "
|
||||
},
|
||||
{ "insert": "any text, and use the editing menu to " },
|
||||
{ "attributes": { "italic": true }, "insert": "style" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "bold": true }, "insert": "your" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "underline": true }, "insert": "writing" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "code": true }, "insert": "however" },
|
||||
{ "insert": " you " },
|
||||
{ "attributes": { "strikethrough": true }, "insert": "like." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{ "insert": "As soon as you type " },
|
||||
{
|
||||
"attributes": { "code": true, "font_color": "0xff00b5ff" },
|
||||
"insert": "/"
|
||||
},
|
||||
{ "insert": " a menu will pop up. Select " },
|
||||
{
|
||||
"attributes": { "bg_color": "0x4d9c27b0" },
|
||||
"insert": "different types"
|
||||
},
|
||||
{ "insert": " of content blocks you can add." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Type " },
|
||||
{ "attributes": { "code": true }, "insert": "/" },
|
||||
{ "insert": " followed by " },
|
||||
{ "attributes": { "code": true }, "insert": "/bullet" },
|
||||
{ "insert": " or " },
|
||||
{ "attributes": { "code": true }, "insert": "/num" },
|
||||
{ "attributes": { "code": false }, "insert": " to create a list." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "+ New Page " },
|
||||
{
|
||||
"insert": "button at the bottom of your sidebar to add a new page."
|
||||
}
|
||||
],
|
||||
"checked": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "+" },
|
||||
{ "insert": " next to any page title in the sidebar to " },
|
||||
{
|
||||
"attributes": { "font_color": "0xff8427e0" },
|
||||
"insert": "quickly"
|
||||
},
|
||||
{ "insert": " add a new subpage, " },
|
||||
{ "attributes": { "code": true }, "insert": "Document" },
|
||||
{ "attributes": { "code": false }, "insert": ", " },
|
||||
{ "attributes": { "code": true }, "insert": "Grid" },
|
||||
{ "attributes": { "code": false }, "insert": ", or " },
|
||||
{ "attributes": { "code": true }, "insert": "Kanban Board" },
|
||||
{ "attributes": { "code": false }, "insert": "." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{ "type": "divider" },
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Keyboard shortcuts, markdown, and code block" }],
|
||||
"level": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Keyboard shortcuts " },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://appflowy.gitbook.io/docs/essential-documentation/shortcuts"
|
||||
},
|
||||
"insert": "guide"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Markdown " },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://appflowy.gitbook.io/docs/essential-documentation/markdown"
|
||||
},
|
||||
"insert": "reference"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Type " },
|
||||
{ "attributes": { "code": true }, "insert": "/code" },
|
||||
{
|
||||
"attributes": { "code": false },
|
||||
"insert": " to insert a code block"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "code",
|
||||
"data": {
|
||||
"language": "rust",
|
||||
"delta": [
|
||||
{
|
||||
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n println!(\"Hello World!\");\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [] },
|
||||
"children": [{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a paragraph" }] },
|
||||
"children": [{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a paragraph" }] }
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "level": 2, "delta": [{ "insert": "Have a question❓" }] }
|
||||
},
|
||||
{
|
||||
"type": "toggle_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "?" },
|
||||
{ "insert": " at the bottom right for help and support." }
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a paragraph" }] }
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a paragraph" }] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "quote",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "?" },
|
||||
{ "insert": " at the bottom right for help and support." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{
|
||||
"type": "callout",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "\nLike AppFlowy? Follow us:\n" },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://github.com/AppFlowy-IO/AppFlowy"
|
||||
},
|
||||
"insert": "GitHub"
|
||||
},
|
||||
{ "insert": "\n" },
|
||||
{
|
||||
"attributes": { "href": "https://twitter.com/appflowy" },
|
||||
"insert": "Twitter"
|
||||
},
|
||||
{ "insert": ": @appflowy\n" },
|
||||
{
|
||||
"attributes": { "href": "https://blog-appflowy.ghost.io/" },
|
||||
"insert": "Newsletter"
|
||||
},
|
||||
{ "insert": "\n" }
|
||||
],
|
||||
"icon": "🥰"
|
||||
}
|
||||
},
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{ "type": "paragraph", "data": { "delta": [] } }
|
||||
]
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [{
|
||||
"type": "math_equation",
|
||||
"data": {
|
||||
"formula": "E = MC^2"
|
||||
}
|
||||
}]
|
||||
}
|
371
frontend/rust-lib/flowy-document/tests/assets/json/notion.json
Normal file
371
frontend/rust-lib/flowy-document/tests/assets/json/notion.json
Normal file
@ -0,0 +1,371 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "The Notion Document"
|
||||
}
|
||||
],
|
||||
"level": 1
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 1,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "Heading-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 2,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "Heading - 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"level": 3,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "Heading - 3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "paragraph’s child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a bulleted list - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a bulleted list - 1 - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a bulleted list - 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "[ ] This is a todo - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "[ ] This is a paragraph - 1-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a numbered list -1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "bulleted_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a toggle list"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a toggle child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "quote",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a quote"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a quote child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "divider",
|
||||
"data": {},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "code",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n **println**!(\"Hello World!\");\n}"
|
||||
}
|
||||
],
|
||||
"language": "jsx"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "<aside>\n 💡 callout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "</aside>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph font-color bg-color "
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"bold": true
|
||||
},
|
||||
"insert": "bold"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"italic": true
|
||||
},
|
||||
"insert": "italic underline "
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"italic": true,
|
||||
"strikethrough": true
|
||||
},
|
||||
"insert": "strike-through"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"code": true,
|
||||
"italic": true
|
||||
},
|
||||
"insert": "inline-code"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"italic": true
|
||||
},
|
||||
"insert": " $inline-formula$ "
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://www.notion.so/The-Notion-Document-d4236da306b84f6199e4091705042d78?pvs=21",
|
||||
"italic": true
|
||||
},
|
||||
"insert": "link"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "$$\n |x| = \\begin{cases}\n x, &\\quad x \\geq 0 \\\\\n -x, &\\quad x < 0\n \\end{cases}\n $$"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Highlight"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "You can also"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "nest"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [
|
||||
{ "insert": "\nLike AppFlowy? Follow us:\n" },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://github.com/AppFlowy-IO/AppFlowy"
|
||||
},
|
||||
"insert": "GitHub"
|
||||
},
|
||||
{ "insert": "\n" },
|
||||
{
|
||||
"attributes": { "href": "https://twitter.com/appflowy" },
|
||||
"insert": "Twitter"
|
||||
},
|
||||
{ "insert": ": @appflowy\n" },
|
||||
{
|
||||
"attributes": { "href": "https://blog-appflowy.ghost.io/" },
|
||||
"insert": "Newsletter"
|
||||
},
|
||||
{ "insert": "\n" }
|
||||
]},
|
||||
"children": [{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "?" },
|
||||
{ "insert": " at the bottom right for help and support." }
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [
|
||||
{
|
||||
"attributes": { "bg_color": "0x4dffeb3b" },
|
||||
"insert": "Highlight "
|
||||
},
|
||||
{ "insert": "any text, and use the editing menu to " },
|
||||
{ "attributes": { "italic": true }, "insert": "style" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "bold": true }, "insert": "your" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "underline": true }, "insert": "writing" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "code": true }, "insert": "however" },
|
||||
{ "insert": " you ", "attributes": { "font_color": "0x4dffeb3b" } },
|
||||
{ "attributes": { "strikethrough": true }, "insert": "like." },
|
||||
{ "attributes": { "formula": true }, "insert": "1+1=2" }
|
||||
] }
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,510 @@
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "# The Notion Document"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "# Heading-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "## Heading - 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "### Heading - 3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "paragraph’s child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "- This is a bulleted list - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " - This is a bulleted list - 1 - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "- This is a bulleted list - 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "- [ ] This is a todo - 1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " - [ ] This is a paragraph - 1-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "1. This is a numbered list -1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "- This is a toggle list"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " This is a toggle child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "> This is a quote"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": ">"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": ">"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "> This is a quote child"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": ">"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "---"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "```jsx"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "// This is the main function."
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "fn main() {"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " // Print text to the console."
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " **println**!(\"Hello World!\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "```"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "<aside>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "💡 callout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "</aside>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "This is a paragraph font-color bg-color **bold** *italic underline ~~strike-through~~ `inline-code` $inline-formula$ [link](https://www.notion.so/The-Notion-Document-d4236da306b84f6199e4091705042d78?pvs=21)*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "$$"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "|x| = \\begin{cases} "
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " x, &\\quad x \\geq 0 \\\\ "
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": " -x, &\\quad x < 0 "
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "\\end{cases}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "$$"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"attributes": null,
|
||||
"insert": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"data": {},
|
||||
"type": "page"
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "quote",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "This is a quote"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "This is a paragraph"
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
100
frontend/rust-lib/flowy-document/tests/assets/json/range_1.json
Normal file
100
frontend/rust-lib/flowy-document/tests/assets/json/range_1.json
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "delta": [{ "insert": " are the basics" }], "level": 2 }
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"data": { "delta": [{ "insert": "Here is H3" }], "level": 3 }
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Click anywhere and just start typing." }],
|
||||
"checked": false
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "Enter" },
|
||||
{ "insert": " to create a new line." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": { "bg_color": "0x4dffeb3b" },
|
||||
"insert": "Highlight "
|
||||
},
|
||||
{ "insert": "any text, and use the editing menu to " },
|
||||
{ "attributes": { "italic": true }, "insert": "style" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "bold": true }, "insert": "your" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "underline": true }, "insert": "writing" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "code": true }, "insert": "however" },
|
||||
{ "insert": " you " },
|
||||
{ "attributes": { "strikethrough": true }, "insert": "like." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{ "insert": "As soon as you type " },
|
||||
{
|
||||
"attributes": { "code": true, "font_color": "0xff00b5ff" },
|
||||
"insert": "/"
|
||||
},
|
||||
{ "insert": " a menu will pop up. Select " },
|
||||
{
|
||||
"attributes": { "bg_color": "0x4d9c27b0" },
|
||||
"insert": "different types"
|
||||
},
|
||||
{ "insert": " of content blocks you can add." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Type " },
|
||||
{ "attributes": { "code": true }, "insert": "/" },
|
||||
{ "insert": " followed by " },
|
||||
{ "attributes": { "code": true }, "insert": "/bullet" },
|
||||
{ "insert": " or " },
|
||||
{ "attributes": { "code": true }, "insert": "/num" },
|
||||
{ "attributes": { "code": false }, "insert": " to create a list." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "+ New" }
|
||||
],
|
||||
"checked": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
178
frontend/rust-lib/flowy-document/tests/assets/json/range_2.json
Normal file
178
frontend/rust-lib/flowy-document/tests/assets/json/range_2.json
Normal file
@ -0,0 +1,178 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "attributes": { "code": true }, "insert": "Enter" },
|
||||
{ "insert": " to create a new line." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{
|
||||
"attributes": { "bg_color": "0x4dffeb3b" },
|
||||
"insert": "Highlight "
|
||||
},
|
||||
{ "insert": "any text, and use the editing menu to " },
|
||||
{ "attributes": { "italic": true }, "insert": "style" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "bold": true }, "insert": "your" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "underline": true }, "insert": "writing" },
|
||||
{ "insert": " " },
|
||||
{ "attributes": { "code": true }, "insert": "however" },
|
||||
{ "insert": " you " },
|
||||
{ "attributes": { "strikethrough": true }, "insert": "like." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{ "insert": "As soon as you type " },
|
||||
{
|
||||
"attributes": { "code": true, "font_color": "0xff00b5ff" },
|
||||
"insert": "/"
|
||||
},
|
||||
{ "insert": " a menu will pop up. Select " },
|
||||
{
|
||||
"attributes": { "bg_color": "0x4d9c27b0" },
|
||||
"insert": "different types"
|
||||
},
|
||||
{ "insert": " of content blocks you can add." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Type " },
|
||||
{ "attributes": { "code": true }, "insert": "/" },
|
||||
{ "insert": " followed by " },
|
||||
{ "attributes": { "code": true }, "insert": "/bullet" },
|
||||
{ "insert": " or " },
|
||||
{ "attributes": { "code": true }, "insert": "/num" },
|
||||
{ "attributes": { "code": false }, "insert": " to create a list." }
|
||||
],
|
||||
"checked": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "+ New Page " },
|
||||
{
|
||||
"insert": "button at the bottom of your sidebar to add a new page."
|
||||
}
|
||||
],
|
||||
"checked": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": false,
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "+" },
|
||||
{ "insert": " next to any page title in the sidebar to " },
|
||||
{
|
||||
"attributes": { "font_color": "0xff8427e0" },
|
||||
"insert": "quickly"
|
||||
},
|
||||
{ "insert": " add a new subpage, " },
|
||||
{ "attributes": { "code": true }, "insert": "Document" },
|
||||
{ "attributes": { "code": false }, "insert": ", " },
|
||||
{ "attributes": { "code": true }, "insert": "Grid" },
|
||||
{ "attributes": { "code": false }, "insert": ", or " },
|
||||
{ "attributes": { "code": true }, "insert": "Kanban Board" },
|
||||
{ "attributes": { "code": false }, "insert": "." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{ "type": "divider" },
|
||||
{ "type": "paragraph", "data": { "delta": [] } },
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Keyboard shortcuts, markdown, and code block" }],
|
||||
"level": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Keyboard shortcuts " },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://appflowy.gitbook.io/docs/essential-documentation/shortcuts"
|
||||
},
|
||||
"insert": "guide"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Markdown " },
|
||||
{
|
||||
"attributes": {
|
||||
"href": "https://appflowy.gitbook.io/docs/essential-documentation/markdown"
|
||||
},
|
||||
"insert": "reference"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "numbered_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Type " },
|
||||
{ "attributes": { "code": true }, "insert": "/code" },
|
||||
{
|
||||
"attributes": { "code": false },
|
||||
"insert": " to insert a code block"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "code",
|
||||
"data": {
|
||||
"language": "rust",
|
||||
"delta": [
|
||||
{
|
||||
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n println!(\"Hello World!\");\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [] },
|
||||
"children": [{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a p" }] },
|
||||
"children": []
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "page",
|
||||
"data": {
|
||||
"delta": [{
|
||||
"insert": "This is a paragraph"
|
||||
}]
|
||||
},
|
||||
"children": []
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"checked": true,
|
||||
"delta": [
|
||||
{
|
||||
"insert": "Highlight"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "You can also"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"checked": false,
|
||||
"data": {
|
||||
"delta": [
|
||||
{
|
||||
"insert": "nest"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "toggle_list",
|
||||
"data": {
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "attributes": { "code": true }, "insert": "?" },
|
||||
{ "insert": " at the bottom right for help and support." }
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"data": { "delta": [{ "insert": "This is a paragraph" }] }
|
||||
},
|
||||
{
|
||||
"type": "toggle_list",
|
||||
"data": { "delta": [{ "insert": "This is a toggle list" }] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
Highlight
|
||||
You can also
|
||||
nest
|
@ -0,0 +1,6 @@
|
||||
🥰
|
||||
Like AppFlowy? Follow us:
|
||||
GitHub
|
||||
Twitter: @appflowy
|
||||
Newsletter
|
||||
|
@ -0,0 +1,5 @@
|
||||
// This is the main function.
|
||||
fn main() {
|
||||
// Print text to the console.
|
||||
println!("Hello World!");
|
||||
}
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,3 @@
|
||||
Heading1
|
||||
Heading2
|
||||
Heading3
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1 @@
|
||||
E = MC^2
|
@ -0,0 +1,3 @@
|
||||
Highlight
|
||||
You can also
|
||||
nest
|
@ -0,0 +1,8 @@
|
||||
|
||||
Like AppFlowy? Follow us:
|
||||
GitHub
|
||||
Twitter: @appflowy
|
||||
Newsletter
|
||||
|
||||
Click ? at the bottom right for help and support.
|
||||
Highlight any text, and use the editing menu to style your writing however you like.1+1=2
|
@ -0,0 +1,64 @@
|
||||
# The Notion Document
|
||||
|
||||
# Heading-1
|
||||
|
||||
## Heading - 2
|
||||
|
||||
### Heading - 3
|
||||
|
||||
This is a paragraph
|
||||
|
||||
paragraph’s child
|
||||
|
||||
- This is a bulleted list - 1
|
||||
- This is a bulleted list - 1 - 1
|
||||
- This is a bulleted list - 2
|
||||
|
||||
This is a paragraph
|
||||
|
||||
- [ ] This is a todo - 1
|
||||
- [ ] This is a paragraph - 1-1
|
||||
1. This is a numbered list -1
|
||||
|
||||
This is a paragraph
|
||||
|
||||
- This is a toggle list
|
||||
|
||||
This is a toggle child
|
||||
|
||||
|
||||
> This is a quote
|
||||
>
|
||||
>
|
||||
> This is a quote child
|
||||
>
|
||||
|
||||
This is a paragraph
|
||||
|
||||
---
|
||||
|
||||
```jsx
|
||||
// This is the main function.
|
||||
fn main() {
|
||||
// Print text to the console.
|
||||
**println**!("Hello World!");
|
||||
}
|
||||
```
|
||||
|
||||
This is a paragraph
|
||||
|
||||
<aside>
|
||||
💡 callout
|
||||
|
||||
</aside>
|
||||
|
||||
This is a paragraph font-color bg-color **bold** *italic underline ~~strike-through~~ `inline-code` $inline-formula$ [link](https://www.notion.so/The-Notion-Document-d4236da306b84f6199e4091705042d78?pvs=21)*
|
||||
|
||||
$$
|
||||
|x| = \begin{cases}
|
||||
x, &\quad x \geq 0 \\
|
||||
-x, &\quad x < 0
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
End
|
@ -0,0 +1,2 @@
|
||||
This is a quote
|
||||
This is a paragraph
|
@ -0,0 +1,3 @@
|
||||
Highlight
|
||||
You can also
|
||||
nest
|
@ -0,0 +1,3 @@
|
||||
Click ? at the bottom right for help and support.
|
||||
This is a paragraph
|
||||
This is a toggle list
|
@ -0,0 +1,39 @@
|
||||
use std::{collections::HashMap, vec};
|
||||
|
||||
use collab_document::blocks::{Block, BlockAction, BlockActionPayload, BlockActionType};
|
||||
use collab_document::document_data::PARAGRAPH_BLOCK_TYPE;
|
||||
|
||||
use crate::document::util;
|
||||
use crate::document::util::gen_id;
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_apply_insert_block_with_empty_parent_id() {
|
||||
let (_, document, page_id) = util::create_and_open_empty_document().await;
|
||||
|
||||
// create a text block with no parent
|
||||
let text_block_id = gen_id();
|
||||
let text_block = Block {
|
||||
id: text_block_id.clone(),
|
||||
ty: PARAGRAPH_BLOCK_TYPE.to_string(),
|
||||
parent: "".to_string(),
|
||||
children: gen_id(),
|
||||
external_id: None,
|
||||
external_type: None,
|
||||
data: HashMap::new(),
|
||||
};
|
||||
let insert_text_action = BlockAction {
|
||||
action: BlockActionType::Insert,
|
||||
payload: BlockActionPayload {
|
||||
block: Some(text_block),
|
||||
parent_id: Some(page_id.clone()),
|
||||
prev_id: None,
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
document.lock().apply_action(vec![insert_text_action]);
|
||||
|
||||
// read the text block and it's parent id should be the page id
|
||||
let block = document.lock().get_block(&text_block_id).unwrap();
|
||||
assert_eq!(block.parent, page_id);
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use collab_document::blocks::{Block, BlockAction, BlockActionPayload, BlockActionType};
|
||||
use collab_document::document_data::{default_document_data, PARAGRAPH_BLOCK_TYPE};
|
||||
|
||||
use crate::document::util::{gen_document_id, gen_id, DocumentTest};
|
||||
|
||||
#[tokio::test]
|
||||
async fn undo_redo_test() {
|
||||
let test = DocumentTest::new();
|
||||
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
|
||||
// create a document
|
||||
_ = test
|
||||
.create_document(test.user.user_id().unwrap(), &doc_id, Some(data.clone()))
|
||||
.await;
|
||||
|
||||
// open a document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let document = document.lock();
|
||||
let page_block = document.get_block(&data.page_id).unwrap();
|
||||
let page_id = page_block.id;
|
||||
let text_block_id = gen_id();
|
||||
|
||||
// insert a text block
|
||||
let text_block = Block {
|
||||
id: text_block_id.clone(),
|
||||
ty: PARAGRAPH_BLOCK_TYPE.to_string(),
|
||||
parent: page_id.clone(),
|
||||
children: gen_id(),
|
||||
external_id: None,
|
||||
external_type: None,
|
||||
data: HashMap::new(),
|
||||
};
|
||||
let insert_text_action = BlockAction {
|
||||
action: BlockActionType::Insert,
|
||||
payload: BlockActionPayload {
|
||||
block: Some(text_block),
|
||||
parent_id: Some(page_id),
|
||||
prev_id: None,
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
document.apply_action(vec![insert_text_action]);
|
||||
|
||||
let can_undo = document.can_undo();
|
||||
assert!(can_undo);
|
||||
// undo the insert
|
||||
let undo = document.undo();
|
||||
assert!(undo);
|
||||
assert_eq!(document.get_block(&text_block_id), None);
|
||||
|
||||
let can_redo = document.can_redo();
|
||||
assert!(can_redo);
|
||||
// redo the insert
|
||||
let redo = document.redo();
|
||||
assert!(redo);
|
||||
}
|
217
frontend/rust-lib/flowy-document/tests/document/document_test.rs
Normal file
217
frontend/rust-lib/flowy-document/tests/document/document_test.rs
Normal file
@ -0,0 +1,217 @@
|
||||
use std::{collections::HashMap, vec};
|
||||
|
||||
use collab_document::blocks::{Block, BlockAction, BlockActionPayload, BlockActionType};
|
||||
use collab_document::document_data::{default_document_data, PARAGRAPH_BLOCK_TYPE};
|
||||
use serde_json::{json, to_value, Value};
|
||||
|
||||
use crate::document::util::{gen_document_id, gen_id, DocumentTest};
|
||||
|
||||
#[tokio::test]
|
||||
async fn restore_document() {
|
||||
let test = DocumentTest::new();
|
||||
|
||||
// create a document
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
test
|
||||
.create_document(uid, &doc_id, Some(data.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
let data_a = test.get_document_data(&doc_id).await.unwrap();
|
||||
assert_eq!(data_a, data);
|
||||
|
||||
let data_b = test
|
||||
.get_document(&doc_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.lock()
|
||||
.get_document_data()
|
||||
.unwrap();
|
||||
// close a document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
assert_eq!(data_b, data);
|
||||
|
||||
// restore
|
||||
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
|
||||
// open a document
|
||||
let data_b = test
|
||||
.get_document(&doc_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.lock()
|
||||
.get_document_data()
|
||||
.unwrap();
|
||||
// close a document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
|
||||
assert_eq!(data_b, data);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_apply_insert_action() {
|
||||
let test = DocumentTest::new();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
|
||||
// create a document
|
||||
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
|
||||
|
||||
// open a document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let page_block = document.lock().get_block(&data.page_id).unwrap();
|
||||
|
||||
// insert a text block
|
||||
let text_block = Block {
|
||||
id: gen_id(),
|
||||
ty: PARAGRAPH_BLOCK_TYPE.to_string(),
|
||||
parent: page_block.id,
|
||||
children: gen_id(),
|
||||
external_id: None,
|
||||
external_type: None,
|
||||
data: HashMap::new(),
|
||||
};
|
||||
let insert_text_action = BlockAction {
|
||||
action: BlockActionType::Insert,
|
||||
payload: BlockActionPayload {
|
||||
parent_id: None,
|
||||
prev_id: None,
|
||||
block: Some(text_block),
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
document.lock().apply_action(vec![insert_text_action]);
|
||||
let data_a = document.lock().get_document_data().unwrap();
|
||||
// close the original document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
|
||||
// re-open the document
|
||||
let data_b = test
|
||||
.get_document(&doc_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.lock()
|
||||
.get_document_data()
|
||||
.unwrap();
|
||||
// close a document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
|
||||
assert_eq!(data_b, data_a);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_apply_update_page_action() {
|
||||
let test = DocumentTest::new();
|
||||
let doc_id: String = gen_document_id();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
let data = default_document_data();
|
||||
|
||||
// create a document
|
||||
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
|
||||
|
||||
// open a document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let page_block = document.lock().get_block(&data.page_id).unwrap();
|
||||
|
||||
let mut page_block_clone = page_block;
|
||||
page_block_clone.data = HashMap::new();
|
||||
page_block_clone.data.insert(
|
||||
"delta".to_string(),
|
||||
to_value(json!([{"insert": "Hello World!"}])).unwrap(),
|
||||
);
|
||||
let action = BlockAction {
|
||||
action: BlockActionType::Update,
|
||||
payload: BlockActionPayload {
|
||||
parent_id: None,
|
||||
prev_id: None,
|
||||
block: Some(page_block_clone),
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
let actions = vec![action];
|
||||
tracing::trace!("{:?}", &actions);
|
||||
document.lock().apply_action(actions);
|
||||
let page_block_old = document.lock().get_block(&data.page_id).unwrap();
|
||||
_ = test.close_document(&doc_id).await;
|
||||
|
||||
// re-open the document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let page_block_new = document.lock().get_block(&data.page_id).unwrap();
|
||||
assert_eq!(page_block_old, page_block_new);
|
||||
assert!(page_block_new.data.contains_key("delta"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_apply_update_action() {
|
||||
let test = DocumentTest::new();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
|
||||
// create a document
|
||||
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
|
||||
|
||||
// open a document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let page_block = document.lock().get_block(&data.page_id).unwrap();
|
||||
|
||||
// insert a text block
|
||||
let text_block_id = gen_id();
|
||||
let text_block = Block {
|
||||
id: text_block_id.clone(),
|
||||
ty: PARAGRAPH_BLOCK_TYPE.to_string(),
|
||||
parent: page_block.id,
|
||||
children: gen_id(),
|
||||
external_id: None,
|
||||
external_type: None,
|
||||
data: HashMap::new(),
|
||||
};
|
||||
let insert_text_action = BlockAction {
|
||||
action: BlockActionType::Insert,
|
||||
payload: BlockActionPayload {
|
||||
block: Some(text_block),
|
||||
parent_id: None,
|
||||
prev_id: None,
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
document.lock().apply_action(vec![insert_text_action]);
|
||||
|
||||
// update the text block
|
||||
let existing_text_block = document.lock().get_block(&text_block_id).unwrap();
|
||||
let mut updated_text_block_data = HashMap::new();
|
||||
updated_text_block_data.insert("delta".to_string(), Value::String("delta".to_string()));
|
||||
let updated_text_block = Block {
|
||||
id: existing_text_block.id,
|
||||
ty: existing_text_block.ty,
|
||||
parent: existing_text_block.parent,
|
||||
children: existing_text_block.children,
|
||||
external_id: None,
|
||||
external_type: None,
|
||||
data: updated_text_block_data.clone(),
|
||||
};
|
||||
let update_text_action = BlockAction {
|
||||
action: BlockActionType::Update,
|
||||
payload: BlockActionPayload {
|
||||
block: Some(updated_text_block),
|
||||
parent_id: None,
|
||||
prev_id: None,
|
||||
delta: None,
|
||||
text_id: None,
|
||||
},
|
||||
};
|
||||
document.lock().apply_action(vec![update_text_action]);
|
||||
// close the original document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
|
||||
// re-open the document
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
let block = document.lock().get_block(&text_block_id).unwrap();
|
||||
assert_eq!(block.data, updated_text_block_data);
|
||||
// close a document
|
||||
_ = test.close_document(&doc_id).await;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
use flowy_document::{
|
||||
entities::{ConvertDataPayloadPB, ConvertType},
|
||||
event_handler::convert_data_to_document_internal,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn convert_json_to_document() {
|
||||
let json_str = r#"
|
||||
{
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph1"
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
let payload = ConvertDataPayloadPB {
|
||||
convert_type: ConvertType::Json,
|
||||
data: json_str.as_bytes().to_vec(),
|
||||
};
|
||||
let document_data = convert_data_to_document_internal(payload).unwrap();
|
||||
|
||||
let page_id = document_data.page_id;
|
||||
let blocks = document_data.blocks;
|
||||
let children_map = document_data.meta.children_map;
|
||||
let page_block = blocks.get(&page_id).unwrap();
|
||||
let page_children = children_map.get(&page_block.children_id).unwrap();
|
||||
assert_eq!(page_children.children.len(), 1);
|
||||
let paragraph1 = blocks.get(page_children.children.first().unwrap()).unwrap();
|
||||
assert_eq!(paragraph1.ty, "paragraph1");
|
||||
assert_eq!(paragraph1.parent_id, page_block.id);
|
||||
}
|
5
frontend/rust-lib/flowy-document/tests/document/mod.rs
Normal file
5
frontend/rust-lib/flowy-document/tests/document/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod document_insert_test;
|
||||
mod document_redo_undo_test;
|
||||
mod document_test;
|
||||
mod event_handler_test;
|
||||
pub mod util;
|
192
frontend/rust-lib/flowy-document/tests/document/util.rs
Normal file
192
frontend/rust-lib/flowy-document/tests/document/util.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
use bytes::Bytes;
|
||||
use collab::core::collab::CollabDocState;
|
||||
use collab::preclude::CollabPlugin;
|
||||
use collab_document::blocks::DocumentData;
|
||||
use collab_document::document_data::default_document_data;
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::Once;
|
||||
use tempfile::TempDir;
|
||||
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
|
||||
use uuid::Uuid;
|
||||
|
||||
use collab_integrate::collab_builder::{
|
||||
AppFlowyCollabBuilder, CollabCloudPluginProvider, CollabPluginProviderContext,
|
||||
CollabPluginProviderType,
|
||||
};
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_document::document::MutexDocument;
|
||||
use flowy_document::manager::{DocumentManager, DocumentUser};
|
||||
use flowy_document_deps::cloud::*;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_storage::{FileStorageService, StorageObject};
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::future::{to_fut, Fut, FutureResult};
|
||||
|
||||
pub struct DocumentTest {
|
||||
inner: DocumentManager,
|
||||
}
|
||||
|
||||
impl DocumentTest {
|
||||
pub fn new() -> Self {
|
||||
let user = FakeUser::new();
|
||||
let cloud_service = Arc::new(LocalTestDocumentCloudServiceImpl());
|
||||
let file_storage = Arc::new(DocumentTestFileStorageService) as Arc<dyn FileStorageService>;
|
||||
let manager = DocumentManager::new(
|
||||
Arc::new(user),
|
||||
default_collab_builder(),
|
||||
cloud_service,
|
||||
Arc::downgrade(&file_storage),
|
||||
);
|
||||
Self { inner: manager }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DocumentTest {
|
||||
type Target = DocumentManager;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FakeUser {
|
||||
collab_db: Arc<RocksCollabDB>,
|
||||
}
|
||||
|
||||
impl FakeUser {
|
||||
pub fn new() -> Self {
|
||||
setup_log();
|
||||
|
||||
let tempdir = TempDir::new().unwrap();
|
||||
let path = tempdir.into_path();
|
||||
let collab_db = Arc::new(RocksCollabDB::open(path).unwrap());
|
||||
|
||||
Self { collab_db }
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentUser for FakeUser {
|
||||
fn user_id(&self) -> Result<i64, FlowyError> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn workspace_id(&self) -> Result<String, FlowyError> {
|
||||
Ok(Uuid::new_v4().to_string())
|
||||
}
|
||||
|
||||
fn token(&self) -> Result<Option<String>, FlowyError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn collab_db(&self, _uid: i64) -> Result<std::sync::Weak<RocksCollabDB>, FlowyError> {
|
||||
Ok(Arc::downgrade(&self.collab_db))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_log() {
|
||||
static START: Once = Once::new();
|
||||
START.call_once(|| {
|
||||
std::env::set_var("RUST_LOG", "collab_persistence=trace");
|
||||
let subscriber = Subscriber::builder()
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.with_ansi(true)
|
||||
.finish();
|
||||
subscriber.try_init().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
|
||||
let builder =
|
||||
AppFlowyCollabBuilder::new(DefaultCollabStorageProvider(), "fake_device_id".to_string());
|
||||
builder.initialize(uuid::Uuid::new_v4().to_string());
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<MutexDocument>, String) {
|
||||
let test = DocumentTest::new();
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
// create a document
|
||||
test
|
||||
.create_document(uid, &doc_id, Some(data.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let document = test.get_document(&doc_id).await.unwrap();
|
||||
|
||||
(test, document, data.page_id)
|
||||
}
|
||||
|
||||
pub fn gen_document_id() -> String {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
uuid.to_string()
|
||||
}
|
||||
|
||||
pub fn gen_id() -> String {
|
||||
nanoid!(10)
|
||||
}
|
||||
|
||||
pub struct LocalTestDocumentCloudServiceImpl();
|
||||
impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
_document_id: &str,
|
||||
_workspace_id: &str,
|
||||
) -> FutureResult<CollabDocState, FlowyError> {
|
||||
FutureResult::new(async move { Ok(vec![]) })
|
||||
}
|
||||
|
||||
fn get_document_snapshots(
|
||||
&self,
|
||||
_document_id: &str,
|
||||
_limit: usize,
|
||||
_workspace_id: &str,
|
||||
) -> FutureResult<Vec<DocumentSnapshot>, Error> {
|
||||
FutureResult::new(async move { Ok(vec![]) })
|
||||
}
|
||||
|
||||
fn get_document_data(
|
||||
&self,
|
||||
_document_id: &str,
|
||||
_workspace_id: &str,
|
||||
) -> FutureResult<Option<DocumentData>, Error> {
|
||||
FutureResult::new(async move { Ok(None) })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DocumentTestFileStorageService;
|
||||
impl FileStorageService for DocumentTestFileStorageService {
|
||||
fn create_object(&self, _object: StorageObject) -> FutureResult<String, FlowyError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn delete_object_by_url(&self, _object_url: String) -> FutureResult<(), FlowyError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_object_by_url(&self, _object_url: String) -> FutureResult<Bytes, FlowyError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultCollabStorageProvider();
|
||||
|
||||
#[async_trait]
|
||||
impl CollabCloudPluginProvider for DefaultCollabStorageProvider {
|
||||
fn provider_type(&self) -> CollabPluginProviderType {
|
||||
CollabPluginProviderType::Local
|
||||
}
|
||||
|
||||
fn get_plugins(&self, _context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
|
||||
to_fut(async move { vec![] })
|
||||
}
|
||||
|
||||
fn is_sync_enabled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
2
frontend/rust-lib/flowy-document/tests/main.rs
Normal file
2
frontend/rust-lib/flowy-document/tests/main.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod document;
|
||||
mod parser;
|
@ -0,0 +1,105 @@
|
||||
use collab_document::blocks::DocumentData;
|
||||
use flowy_document::parser::document_data_parser::DocumentDataParser;
|
||||
use flowy_document::parser::json::parser::JsonToDocumentParser;
|
||||
use flowy_document::parser::parser_entities::{NestedBlock, Range, Selection};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_data_parse_json_test() {
|
||||
let initial_json_str = include_str!("../assets/json/initial_document.json");
|
||||
let document_data = JsonToDocumentParser::json_str_to_document(initial_json_str)
|
||||
.unwrap()
|
||||
.into();
|
||||
let parser = DocumentDataParser::new(Arc::new(document_data), None);
|
||||
let read_me_json = serde_json::from_str::<NestedBlock>(initial_json_str).unwrap();
|
||||
let json = parser.to_json().unwrap();
|
||||
assert_eq!(read_me_json, json);
|
||||
}
|
||||
|
||||
// range_1 is a range from the 2nd block to the 8th block
|
||||
#[tokio::test]
|
||||
async fn document_data_to_json_with_range_1_test() {
|
||||
let initial_json_str = include_str!("../assets/json/initial_document.json");
|
||||
let document_data: DocumentData = JsonToDocumentParser::json_str_to_document(initial_json_str)
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let children_map = &document_data.meta.children_map;
|
||||
let page_block_id = &document_data.page_id;
|
||||
let blocks = &document_data.blocks;
|
||||
let page_block = blocks.get(page_block_id).unwrap();
|
||||
let children = children_map.get(page_block.children.as_str()).unwrap();
|
||||
|
||||
let range = Range {
|
||||
start: Selection {
|
||||
block_id: children.get(1).unwrap().to_string(),
|
||||
index: 4,
|
||||
length: 15,
|
||||
},
|
||||
end: Selection {
|
||||
block_id: children.get(7).unwrap().to_string(),
|
||||
index: 0,
|
||||
length: 11,
|
||||
},
|
||||
};
|
||||
let parser = DocumentDataParser::new(Arc::new(document_data), Some(range));
|
||||
let json = parser.to_json().unwrap();
|
||||
let part_1 = include_str!("../assets/json/range_1.json");
|
||||
let part_1_json = serde_json::from_str::<NestedBlock>(part_1).unwrap();
|
||||
assert_eq!(part_1_json, json);
|
||||
}
|
||||
|
||||
// range_2 is a range from the 4th block's first child to the 18th block's first child
|
||||
#[tokio::test]
|
||||
async fn document_data_to_json_with_range_2_test() {
|
||||
let initial_json_str = include_str!("../assets/json/initial_document.json");
|
||||
let document_data: DocumentData = JsonToDocumentParser::json_str_to_document(initial_json_str)
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let children_map = &document_data.meta.children_map;
|
||||
let page_block_id = &document_data.page_id;
|
||||
let blocks = &document_data.blocks;
|
||||
let page_block = blocks.get(page_block_id).unwrap();
|
||||
|
||||
let start_block_parent_id = children_map
|
||||
.get(page_block.children.as_str())
|
||||
.unwrap()
|
||||
.get(3)
|
||||
.unwrap();
|
||||
let start_block_parent = blocks.get(start_block_parent_id).unwrap();
|
||||
let start_block_id = children_map
|
||||
.get(start_block_parent.children.as_str())
|
||||
.unwrap()
|
||||
.first()
|
||||
.unwrap();
|
||||
|
||||
let start = Selection {
|
||||
block_id: start_block_id.to_string(),
|
||||
index: 6,
|
||||
length: 27,
|
||||
};
|
||||
|
||||
let end_block_parent_id = children_map
|
||||
.get(page_block.children.as_str())
|
||||
.unwrap()
|
||||
.get(17)
|
||||
.unwrap();
|
||||
let end_block_parent = blocks.get(end_block_parent_id).unwrap();
|
||||
let end_block_children = children_map
|
||||
.get(end_block_parent.children.as_str())
|
||||
.unwrap();
|
||||
let end_block_id = end_block_children.first().unwrap();
|
||||
let end = Selection {
|
||||
block_id: end_block_id.to_string(),
|
||||
index: 0,
|
||||
length: 11,
|
||||
};
|
||||
|
||||
let range = Range { start, end };
|
||||
let parser = DocumentDataParser::new(Arc::new(document_data), Some(range));
|
||||
let json = parser.to_json().unwrap();
|
||||
let part_2 = include_str!("../assets/json/range_2.json");
|
||||
let part_2_json = serde_json::from_str::<NestedBlock>(part_2).unwrap();
|
||||
assert_eq!(part_2_json, json);
|
||||
}
|
@ -0,0 +1 @@
|
||||
mod parser_test;
|
@ -0,0 +1,45 @@
|
||||
use flowy_document::parser::external::parser::ExternalDataToNestedJSONParser;
|
||||
use flowy_document::parser::parser_entities::{InputType, NestedBlock};
|
||||
|
||||
macro_rules! generate_test_cases {
|
||||
($($ty:ident),*) => {
|
||||
[
|
||||
$(
|
||||
(
|
||||
include_str!(concat!("../../assets/json/", stringify!($ty), ".json")),
|
||||
include_str!(concat!("../../assets/html/", stringify!($ty), ".html")),
|
||||
)
|
||||
),*
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/// test convert data to json
|
||||
/// - input html: <p>Hello</p><p> World!</p>
|
||||
#[tokio::test]
|
||||
async fn html_to_document_test() {
|
||||
let test_cases = generate_test_cases!(notion, google_docs, simple);
|
||||
|
||||
for (json, html) in test_cases.iter() {
|
||||
let parser = ExternalDataToNestedJSONParser::new(html.to_string(), InputType::Html);
|
||||
let block = parser.to_nested_block();
|
||||
assert!(block.is_some());
|
||||
let block = block.unwrap();
|
||||
let expect_block = serde_json::from_str::<NestedBlock>(json).unwrap();
|
||||
assert_eq!(block, expect_block);
|
||||
}
|
||||
}
|
||||
|
||||
/// test convert data to json
|
||||
/// - input plain text: Hello World!
|
||||
#[tokio::test]
|
||||
async fn plain_text_to_document_test() {
|
||||
let plain_text = include_str!("../../assets/text/plain_text.txt");
|
||||
let parser = ExternalDataToNestedJSONParser::new(plain_text.to_string(), InputType::PlainText);
|
||||
let block = parser.to_nested_block();
|
||||
assert!(block.is_some());
|
||||
let block = block.unwrap();
|
||||
let expect_json = include_str!("../../assets/json/plain_text.json");
|
||||
let expect_block = serde_json::from_str::<NestedBlock>(expect_json).unwrap();
|
||||
assert_eq!(block, expect_block);
|
||||
}
|
103
frontend/rust-lib/flowy-document/tests/parser/json/block_test.rs
Normal file
103
frontend/rust-lib/flowy-document/tests/parser/json/block_test.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use serde_json::json;
|
||||
|
||||
use flowy_document::parser::json::block::SerdeBlock;
|
||||
|
||||
#[test]
|
||||
fn test_empty_data_and_children() {
|
||||
let json = json!({
|
||||
"type": "page",
|
||||
});
|
||||
let block = serde_json::from_value::<SerdeBlock>(json).unwrap();
|
||||
assert_eq!(block.ty, "page");
|
||||
assert!(block.data.is_empty());
|
||||
assert!(block.children.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data() {
|
||||
let json = json!({
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Click anywhere and just start typing." }],
|
||||
"checked": false
|
||||
}
|
||||
});
|
||||
let block = serde_json::from_value::<SerdeBlock>(json).unwrap();
|
||||
assert_eq!(block.ty, "todo_list");
|
||||
assert_eq!(block.data.len(), 2);
|
||||
assert_eq!(block.data.get("checked").unwrap(), false);
|
||||
assert_eq!(
|
||||
block.data.get("delta").unwrap().to_owned(),
|
||||
json!([{ "insert": "Click anywhere and just start typing." }])
|
||||
);
|
||||
assert!(block.children.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_children() {
|
||||
let json = json!({
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Welcome to AppFlowy!" }],
|
||||
"level": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "todo_list",
|
||||
"data": {
|
||||
"delta": [{ "insert": "Welcome to AppFlowy!" }],
|
||||
"checked": false
|
||||
}
|
||||
}
|
||||
]});
|
||||
let block = serde_json::from_value::<SerdeBlock>(json).unwrap();
|
||||
assert!(block.data.is_empty());
|
||||
assert_eq!(block.ty, "page");
|
||||
assert_eq!(block.children.len(), 2);
|
||||
// heading
|
||||
let heading = &block.children[0];
|
||||
assert_eq!(heading.ty, "heading");
|
||||
assert_eq!(heading.data.len(), 2);
|
||||
|
||||
// todo_list
|
||||
let todo_list = &block.children[1];
|
||||
assert_eq!(todo_list.ty, "todo_list");
|
||||
assert_eq!(todo_list.data.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_children() {
|
||||
let json = json!({
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
let block = serde_json::from_value::<SerdeBlock>(json).unwrap();
|
||||
assert!(block.data.is_empty());
|
||||
assert_eq!(block.ty, "page");
|
||||
assert_eq!(
|
||||
block.children[0].children[0].children[0].children[0].ty,
|
||||
"paragraph"
|
||||
);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
mod block_test;
|
||||
mod parser_test;
|
@ -0,0 +1,123 @@
|
||||
use collab_document::blocks::json_str_to_hashmap;
|
||||
use flowy_document::parser::json::parser::JsonToDocumentParser;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn test_parser_children_in_order() {
|
||||
let json = json!({
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph1",
|
||||
},
|
||||
{
|
||||
"type": "paragraph2",
|
||||
},
|
||||
{
|
||||
"type": "paragraph3",
|
||||
},
|
||||
{
|
||||
"type": "paragraph4",
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let document = JsonToDocumentParser::json_str_to_document(json.to_string().as_str()).unwrap();
|
||||
|
||||
// root + 4 paragraphs
|
||||
assert_eq!(document.blocks.len(), 5);
|
||||
|
||||
// root + 4 paragraphs
|
||||
assert_eq!(document.meta.children_map.len(), 5);
|
||||
|
||||
let (page_id, page_block) = document
|
||||
.blocks
|
||||
.iter()
|
||||
.find(|(_, block)| block.ty == "page")
|
||||
.unwrap();
|
||||
|
||||
// the children should be in order
|
||||
let page_children = document
|
||||
.meta
|
||||
.children_map
|
||||
.get(page_block.children_id.as_str())
|
||||
.unwrap();
|
||||
assert_eq!(page_children.children.len(), 4);
|
||||
for (i, child_id) in page_children.children.iter().enumerate() {
|
||||
let child = document.blocks.get(child_id).unwrap();
|
||||
assert_eq!(child.ty, format!("paragraph{}", i + 1));
|
||||
assert_eq!(child.parent_id, page_id.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_nested_children() {
|
||||
let json = json!({
|
||||
"type": "page",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let document = JsonToDocumentParser::json_str_to_document(json.to_string().as_str()).unwrap();
|
||||
|
||||
// root + 4 paragraphs
|
||||
assert_eq!(document.blocks.len(), 5);
|
||||
|
||||
// root + 4 paragraphs
|
||||
assert_eq!(document.meta.children_map.len(), 5);
|
||||
|
||||
let (page_id, page_block) = document
|
||||
.blocks
|
||||
.iter()
|
||||
.find(|(_, block)| block.ty == "page")
|
||||
.unwrap();
|
||||
|
||||
// first child of root is a paragraph
|
||||
let page_children = document
|
||||
.meta
|
||||
.children_map
|
||||
.get(page_block.children_id.as_str())
|
||||
.unwrap();
|
||||
assert_eq!(page_children.children.len(), 1);
|
||||
let page_first_child_id = page_children.children.first().unwrap();
|
||||
let page_first_child = document.blocks.get(page_first_child_id).unwrap();
|
||||
assert_eq!(page_first_child.ty, "paragraph");
|
||||
assert_eq!(page_first_child.parent_id, page_id.to_owned());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn parse_readme_test() {
|
||||
let json = include_str!("../../../../flowy-core/assets/read_me.json");
|
||||
let document = JsonToDocumentParser::json_str_to_document(json).unwrap();
|
||||
|
||||
document.blocks.iter().for_each(|(_, block)| {
|
||||
let data = json_str_to_hashmap(&block.data).ok();
|
||||
assert!(data.is_some());
|
||||
if let Some(data) = data {
|
||||
assert!(data.get("delta").is_none());
|
||||
}
|
||||
|
||||
if let Some(external_id) = &block.external_id {
|
||||
let text = document.meta.text_map.get(external_id);
|
||||
assert!(text.is_some());
|
||||
}
|
||||
});
|
||||
}
|
4
frontend/rust-lib/flowy-document/tests/parser/mod.rs
Normal file
4
frontend/rust-lib/flowy-document/tests/parser/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod document_data_parser_test;
|
||||
mod html;
|
||||
mod json;
|
||||
mod parse_to_html_text;
|
@ -0,0 +1,2 @@
|
||||
mod test;
|
||||
mod utils;
|
@ -0,0 +1,37 @@
|
||||
use crate::parser::parse_to_html_text::utils::{assert_document_html_eq, assert_document_text_eq};
|
||||
|
||||
macro_rules! generate_test_cases {
|
||||
($($block_ty:ident),*) => {
|
||||
[
|
||||
$(
|
||||
(
|
||||
include_str!(concat!("../../assets/json/", stringify!($block_ty), ".json")),
|
||||
include_str!(concat!("../../assets/html/", stringify!($block_ty), ".html")),
|
||||
include_str!(concat!("../../assets/text/", stringify!($block_ty), ".txt")),
|
||||
)
|
||||
),*
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn block_tests() {
|
||||
let test_cases = generate_test_cases!(
|
||||
heading,
|
||||
callout,
|
||||
paragraph,
|
||||
divider,
|
||||
image,
|
||||
math_equation,
|
||||
code,
|
||||
bulleted_list,
|
||||
numbered_list,
|
||||
todo_list,
|
||||
toggle_list,
|
||||
quote
|
||||
);
|
||||
for (json_data, expect_html, expect_text) in test_cases.iter() {
|
||||
assert_document_html_eq(json_data, expect_html);
|
||||
assert_document_text_eq(json_data, expect_text);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
use flowy_document::parser::document_data_parser::DocumentDataParser;
|
||||
use flowy_document::parser::json::parser::JsonToDocumentParser;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn assert_document_html_eq(source: &str, expect: &str) {
|
||||
let document_data = JsonToDocumentParser::json_str_to_document(source)
|
||||
.unwrap()
|
||||
.into();
|
||||
let parser = DocumentDataParser::new(Arc::new(document_data), None);
|
||||
let html = parser.to_html();
|
||||
assert_eq!(expect, html);
|
||||
}
|
||||
|
||||
pub fn assert_document_text_eq(source: &str, expect: &str) {
|
||||
let document_data = JsonToDocumentParser::json_str_to_document(source)
|
||||
.unwrap()
|
||||
.into();
|
||||
let parser = DocumentDataParser::new(Arc::new(document_data), None);
|
||||
let text = parser.to_text();
|
||||
assert_eq!(expect, text);
|
||||
}
|
Reference in New Issue
Block a user