diff --git a/test/index.js b/test/index.js
index 53154e9b1..78c199fdf 100644
--- a/test/index.js
+++ b/test/index.js
@@ -11,6 +11,7 @@ import 'babel-polyfill'
* Tests.
*/
+import './plugins'
import './rendering'
import './schema'
import './serializers'
diff --git a/test/plugins/fixtures/custom-rules/index.js b/test/plugins/fixtures/custom-rules/index.js
new file mode 100644
index 000000000..3bffc0643
--- /dev/null
+++ b/test/plugins/fixtures/custom-rules/index.js
@@ -0,0 +1,28 @@
+
+import { Mark } from '../../../..'
+
+const BOLD = {
+ fontWeight: 'bold'
+}
+
+function decorate(text, block) {
+ let { characters } = text
+ let second = characters.get(1)
+ const mark = Mark.create({ type: 'bold' })
+ const marks = second.marks.add(mark)
+ second = second.merge({ marks })
+ characters = characters.set(1, second)
+ return characters
+}
+
+export const plugins = [{
+ schema: {
+ marks: {
+ bold: BOLD
+ },
+ rules: [{
+ match: () => true,
+ decorate,
+ }]
+ }
+}]
diff --git a/test/plugins/fixtures/custom-rules/input.yaml b/test/plugins/fixtures/custom-rules/input.yaml
new file mode 100644
index 000000000..9c6aefff6
--- /dev/null
+++ b/test/plugins/fixtures/custom-rules/input.yaml
@@ -0,0 +1,8 @@
+
+nodes:
+ - kind: block
+ type: default
+ nodes:
+ - kind: text
+ ranges:
+ - text: word
diff --git a/test/plugins/fixtures/custom-rules/output.html b/test/plugins/fixtures/custom-rules/output.html
new file mode 100644
index 000000000..f8a474857
--- /dev/null
+++ b/test/plugins/fixtures/custom-rules/output.html
@@ -0,0 +1,4 @@
+
diff --git a/test/plugins/index.js b/test/plugins/index.js
new file mode 100644
index 000000000..b69c2c783
--- /dev/null
+++ b/test/plugins/index.js
@@ -0,0 +1,65 @@
+
+import React from 'react'
+import ReactDOM from 'react-dom/server'
+import assert from 'assert'
+import cheerio from 'cheerio'
+import fs from 'fs-promise'
+import readYaml from 'read-yaml-promise'
+import { Editor, Raw } from '../..'
+import { resolve } from 'path'
+
+/**
+ * Tests.
+ */
+
+describe.only('plugins', () => {
+ const tests = fs.readdirSync(resolve(__dirname, './fixtures'))
+
+ for (const test of tests) {
+ if (test[0] === '.') continue
+
+ it(test, async () => {
+ const dir = resolve(__dirname, './fixtures', test)
+ const input = await readYaml(resolve(dir, 'input.yaml'))
+ const output = await fs.readFile(resolve(dir, 'output.html'), 'utf8')
+ const props = {
+ state: Raw.deserialize(input, { terse: true }),
+ onChange: () => {},
+ ...require(dir)
+ }
+
+ const string = ReactDOM.renderToStaticMarkup()
+ const expected = cheerio
+ .load(output)
+ .html()
+ .trim()
+ .replace(/\n/gm, '')
+ .replace(/>\s*<')
+
+ assert.equal(clean(string), expected)
+ })
+ }
+})
+
+/**
+ * Clean a renderer `html` string, removing dynamic attributes.
+ *
+ * @param {String} html
+ * @return {String}
+ */
+
+function clean(html) {
+ const $ = cheerio.load(html)
+
+ $('*').each((i, el) => {
+ $(el).removeAttr('data-key')
+ $(el).removeAttr('data-offset-key')
+ })
+
+ $.root().children().removeAttr('autocorrect')
+ $.root().children().removeAttr('spellcheck')
+ $.root().children().removeAttr('style')
+ $.root().children().removeAttr('data-gramm')
+
+ return $.html()
+}