Repository: decaporg/decap-cms (JavaScript)

Issue:
kitchen sink test will always set hasChanged to true
**Describe the bug**

The current kitchen sink test includes example markdown, which will always toggle `hasChanged` when viewed with the `VisualEditor`.

**To Reproduce**

1. clone repo, change content of `dev-test` folder to include minimal `index.html` (just loading the cms) and following `config.yml`:
```yaml
backend:
  name: test-repo
local_backend: true
media_folder: static/images
collections:
  - name: posts
    label: Posts
    create: true
    folder: posts
    editor:
      preview: false
    fields:
      - name: title
        label: Title
        widget: string
      - name: markdown
        label: Markdown
        widget: markdown
```
2. add `posts/my-post.md` with following content lifted from the kitchen sink test:
```md
---
title: My post
markdown: "Fill to_*this*_mark, and your charge is but a penny; to_*this*_a penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling.\n\nUpon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens of_*skrimshander*."
---
```

3. this will toggle `hasChanged` because the initial document value produced by `markdownToSlate` differs from slate's. putting a breakpoint [here](https://github.com/netlify/netlify-cms/blob/ac2491f660e687b47997920092301c8f52dc504c/packages/netlify-cms-widget-markdown/src/MarkdownControl/VisualEditor.js#L169) i see:
<details>
<summary><code>this.state.value.document</code></summary>

```json
{
  "object": "document",
  "data": {},
  "nodes": [
    {
      "object": "block",
      "type": "paragraph",
      "data": {},
      "nodes": [
        {
          "object": "text",
          "text": "Fill to",
          "marks": []
        },
        {
          "object": "text",
          "text": "this",
          "marks": [
            {
              "object": "mark",
              "type": "italic",
              "data": {}
            }
          ]
        },
        {
          "object": "text",
          "text": "_mark, and your charge is but a penny; tothisa penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling. Upon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens ofskrimshander",
          "marks": [
            {
              "object": "mark",
              "type": "italic",
              "data": {}
            }
          ]
        },
        {
          "object": "text",
          "text": ". I sought the landlord, and telling him I desired to be accommodated with a room, received for answer that his house was full—not a bed unoccupied.",
          "marks": []
        }
      ]
    }
  ]
}
```

</details>

<details>
<summary><code>editor.value.document</code></summary>

```json
{
  "object": "document",
  "data": {},
  "nodes": [
    {
      "object": "block",
      "type": "paragraph",
      "data": {},
      "nodes": [
        {
          "object": "text",
          "text": "Fill to",
          "marks": []
        },
        {
          "object": "text",
          "text": "this_mark, and your charge is but a penny; tothisa penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling. Upon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens ofskrimshander",
          "marks": [
            {
              "object": "mark",
              "type": "italic",
              "data": {}
            }
          ]
        },
        {
          "object": "text",
          "text": ". I sought the landlord, and telling him I desired to be accommodated with a room, received for answer that his house was full—not a bed unoccupied.",
          "marks": []
        }
      ]
    }
  ]
}
```

</details>

**Expected behavior**
should not toggle `hasChanged` on initial load

**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->

**Applicable Versions:**
 - Netlify CMS version: current master

**CMS configuration**
see above

**Additional context**



Discussion:

Patch:
diff --git a/packages/netlify-cms-widget-markdown/src/serializers/remarkSlate.js b/packages/netlify-cms-widget-markdown/src/serializers/remarkSlate.js
index f2fba68571b7..833cd85fa9ba 100644
--- a/packages/netlify-cms-widget-markdown/src/serializers/remarkSlate.js
+++ b/packages/netlify-cms-widget-markdown/src/serializers/remarkSlate.js
@@ -230,7 +230,9 @@ export default function remarkToSlate({ voidCodeBlock } = {}) {
      * mark nodes, if any.
      */
     const markType = markMap[node.type];
-    const marks = markType ? [...parentMarks, { type: markMap[node.type] }] : parentMarks;
+    const marks = markType
+      ? [...parentMarks.filter(({ type }) => type !== markType), { type: markType }]
+      : parentMarks;
 
     const children = flatMap(node.children, child => processMarkChild(child, marks));
 


Test patch:
diff --git a/packages/netlify-cms-widget-markdown/src/serializers/__tests__/__fixtures__/issue_3280.md b/packages/netlify-cms-widget-markdown/src/serializers/__tests__/__fixtures__/issue_3280.md
new file mode 100644
index 000000000000..2d6cad7de1d4
--- /dev/null
+++ b/packages/netlify-cms-widget-markdown/src/serializers/__tests__/__fixtures__/issue_3280.md
@@ -0,0 +1,1 @@
+Fill to_*this*_mark, and your charge is but a penny; to_*this*_a penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling.\n\nUpon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens of_*skrimshander*.
\ No newline at end of file
diff --git a/packages/netlify-cms-widget-markdown/src/serializers/__tests__/index.spec.js b/packages/netlify-cms-widget-markdown/src/serializers/__tests__/index.spec.js
new file mode 100644
index 000000000000..c8bfa60e1cb4
--- /dev/null
+++ b/packages/netlify-cms-widget-markdown/src/serializers/__tests__/index.spec.js
@@ -0,0 +1,41 @@
+import path from 'path';
+import fs from 'fs';
+import { markdownToSlate } from '../';
+
+describe('markdownToSlate', () => {
+  it('should handle use case from issue 3280', () => {
+    const mdast = fs.readFileSync(path.join(__dirname, '__fixtures__', 'issue_3280.md'));
+    const slate = markdownToSlate(mdast);
+
+    expect(slate).toEqual({
+      object: 'block',
+      type: 'root',
+      nodes: [
+        {
+          object: 'block',
+          type: 'paragraph',
+          nodes: [
+            {
+              object: 'text',
+              text: 'Fill to',
+            },
+            {
+              object: 'text',
+              text:
+                'this_mark, and your charge is but a penny; tothisa penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling.\\n\\nUpon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens ofskrimshander',
+              marks: [
+                {
+                  type: 'italic',
+                },
+              ],
+            },
+            {
+              object: 'text',
+              text: '.',
+            },
+          ],
+        },
+      ],
+    });
+  });
+});