diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/collab.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/collab.test.js
index 72a285849c6..1524b7dea1d 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/collab.test.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/collab.test.js
@@ -56,9 +56,9 @@ const testSameHistory = clientInfos => {
window.chai
.expect(firstClientInfo.editor._historySteps[i].id)
.to.be.equal(
- clientInfo.editor._historySteps[i].id,
- `History steps are not consistent accross clients.`,
- );
+ clientInfo.editor._historySteps[i].id,
+ `History steps are not consistent accross clients.`,
+ );
} catch (e) {
console.log(
'Clients steps:',
@@ -233,7 +233,7 @@ describe('Collaboration', () => {
it('all client steps should be on the same order', () => {
testMultiEditor({
clientIds: ['c1', 'c2', 'c3'],
- contentBefore: '
ab[c1}{c1][c2}{c2]c
', afterCreate: clientInfos => { applyConcurentActions(clientInfos, { c1: editor => { @@ -314,13 +314,13 @@ describe('Collaboration', () => { mergeClientsSteps(clientInfos); testSameHistory(clientInfos); }, - contentAfter: 'a[c2}{c2]c[c1}{c1]dc', + contentAfter: 'a[c2}{c2]c[c1}{c1]dc
', }); }); it('should insertText twice with client 1 and deleteBackward twice with client 2', () => { testMultiEditor({ clientIds: ['c1', 'c2'], - contentBefore: 'ab[c1}{c1][c2}{c2]c', + contentBefore: 'ab[c1}{c1][c2}{c2]c
', afterCreate: clientInfos => { applyConcurentActions(clientInfos, { c1: editor => { @@ -335,14 +335,14 @@ describe('Collaboration', () => { mergeClientsSteps(clientInfos); testSameHistory(clientInfos); }, - contentAfter: '[c2}{c2]cd[c1}{c1]ec', + contentAfter: '[c2}{c2]cd[c1}{c1]ec
', }); }); }); it('should reset from snapshot', () => { testMultiEditor({ clientIds: ['c1', 'c2'], - contentBefore: 'a[c1}{c1]', + contentBefore: 'a[c1}{c1]
', afterCreate: clientInfos => { clientInfos.c1.editor.execCommand('insertText', 'b'); clientInfos.c1.editor._historyMakeSnapshot(); @@ -356,16 +356,16 @@ describe('Collaboration', () => { ]); chai.expect( clientInfos.c2.editor._historySteps[0].mutations.map(x => x.id), - ).to.deep.equal(['fake_id_1', 'fake_concurent_id_1']); + ).to.deep.equal(['fake_id_1']); }, - contentAfter: 'ab[c1}{c1]', + contentAfter: 'ab[c1}{c1]
', }); }); describe('steps whith no parent in history', () => { it('should be able to retreive steps when disconnected from clients that has send step', () => { testMultiEditor({ clientIds: ['c1', 'c2', 'c3'], - contentBefore: 'a[c1}{c1]b[c2}{c2]
', afterCreate: clientInfos => { clientInfos.c1.editor.execCommand('insertText', 'c'); clientInfos.c2.editor.onExternalHistorySteps([ @@ -425,7 +425,7 @@ describe('Collaboration', () => { clientInfos.c2.editor._historySteps[4], ]); }, - contentAfter: 'ac[c1}{c1]bdef[c2}{c2]', + contentAfter: 'ac[c1}{c1]bdef[c2}{c2]
', }); }); }); @@ -433,7 +433,7 @@ describe('Collaboration', () => { it('should sanitize when adding a node', () => { testMultiEditor({ clientIds: ['c1', 'c2'], - contentBefore: 'a[c1}{c1][c2}{c2]
', afterCreate: clientInfos => { const i = document.createElement('i'); i.innerHTML = 'b'; @@ -464,7 +464,7 @@ describe('Collaboration', () => { }, afterCursorInserted: clientInfos => { chai.expect(clientInfos.c2.editable.innerHTML).to.equal( - 'a[c1}{c1][c2}{c2]
b', ); }, }); @@ -472,7 +472,7 @@ describe('Collaboration', () => { it('should sanitize when changing an attribute', () => { testMultiEditor({ clientIds: ['c1', 'c2'], - contentBefore: 'a
a
a
a
', afterCreate: clientInfos => { const script = document.createElement('script'); script.innerHTML = 'console.log("xss")'; @@ -513,14 +513,14 @@ describe('Collaboration', () => { clientInfos.c2.editor.historyUndo(); window.chai .expect(clientInfos.c2.editor.editable.innerHTML) - .to.equal('a
'); }, }); }); it('should sanitize when undo is adding a descendant script node', () => { testMultiEditor({ clientIds: ['c1', 'c2'], - contentBefore: 'a
', afterCreate: clientInfos => { const div = document.createElement('div'); div.innerHTML = 'b'; @@ -539,14 +539,14 @@ describe('Collaboration', () => { clientInfos.c2.editor.historyUndo(); window.chai .expect(clientInfos.c2.editor.editable.innerHTML) - .to.equal('a
a
a
c`; + + await testEditor(BasicEditor, { + contentBefore: '
123[]4
', + stepFunction: async editor => { + await pasteHtml(editor, `a<${node.toLowerCase()}>b${node.toLowerCase()}>c`); + }, + contentAfter: '123' + html + '[]4
', + }); + } + } + + }); + it('should keep whitelisted Tags tag (2)', async () => { const tagsToKeep = [ 'a
d', // img tag
'a123[]
', stepFunction: async editor => { await pasteHtml(editor, tagToKeep); }, - contentAfter: '123' + tagToKeep + '[]', + contentAfter: '123' + tagToKeep + '[]
', }); } }); it('should keep tables Tags tag and add classes', async () => { await testEditor(BasicEditor, { - contentBefore: '123[]', + contentBefore: '123[]
', stepFunction: async editor => { await pasteHtml(editor, 'a| h |
|---|
| b |
| h |
|---|
| b |
123a
| h |
|---|
| b |
d[]
', }); }); it('should not keep span', async () => { await testEditor(BasicEditor, { - contentBefore: '123[]', + contentBefore: '123[]
', stepFunction: async editor => { await pasteHtml(editor, 'abcd'); }, - contentAfter: '123abcd[]', + contentAfter: '123abcd[]
', }); }); it('should not keep orphan LI', async () => { await testEditor(BasicEditor, { - contentBefore: '123[]', + contentBefore: '123[]
', stepFunction: async editor => { await pasteHtml(editor, 'abc
d[]', + contentAfter: '123a
bc
d[]
', }); }); it('should keep LI in UL', async () => { await testEditor(BasicEditor, { - contentBefore: '123[]', + contentBefore: '123[]
', stepFunction: async editor => { await pasteHtml(editor, 'a123a
d[]
', + }); + }); + it('should keep P and B and not span', async () => { + await testEditor(BasicEditor, { + contentBefore: '123[]xx
', + stepFunction: async editor => { + await pasteHtml(editor, 'abc
defgh'); + }, + contentAfter: '123a
bc
defgh[]xx
', }); }); it('should keep styled span', async () => { await testEditor(BasicEditor, { - contentBefore: '123[]', + contentBefore: '123[]
', stepFunction: async editor => { await pasteHtml(editor, 'abcd'); }, - contentAfter: '123abcd[]', + contentAfter: '123abcd[]
', }); }); }); diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js index a55b460fc6a..3a214752a63 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js @@ -21,6 +21,70 @@ async function twoDeleteForward(editor) { } describe('Editor', () => { + describe('init', () => { + describe('No orphan inline elements compatibility mode', () => { + it('should transform root', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: 'ab
c',
+ contentAfter: '
ab
c
', + }); + }); + it('should keepab
c
', + }); + }); + it('should keep multiple conecutiveab
c
', + }); + }); + it('should transform complexab
c
dxxxe
f
', + }); + }); + it('should transform complexab
c
f
g
', + }); + }); + it('should not transform', async () => { + await testEditor(BasicEditor, { + contentBefore: '
ab
c
ab
c
ab
c
d
ab
c
d
ab
c
xx
ab
c
d
yy
', + }); + }); + it('should not transform indentation', async () => { + await testEditor(BasicEditor, { + contentBefore: ` +ab
+c
`, + contentAfter: ` +ab
+c
`, + }); + }); + it('should transform root .fa', async () => { + await testEditor(BasicEditor, { + contentBefore: 'ab
c
', + contentAfter: 'ab
c
', + }); + }); + }); + }); describe('deleteForward', () => { describe('Selection collapsed', () => { describe('Basic', () => { @@ -310,6 +374,11 @@ X[] stepFunction: deleteForward, contentAfter: 'abc[]def
', }); + await testEditor(BasicEditor, { + contentBefore: 'abc[]
def
orphan node', + stepFunction: deleteForward, + contentAfter: 'abc[]def
orphan node
', + }); }); it('should delete the space if the secondis display inline', async () => { await testEditor(BasicEditor, { @@ -1367,11 +1436,11 @@ X[] }); it('should remove a fontawesome', async () => { await testEditor(BasicEditor, { - contentBefore: `
abc
[]def
`, + contentBefore: `abc
[]def
abc
[]def
`, + contentAfter: `abc
[]def
[]cd
', + contentBefore: '[]cd
[]cd
ef', + contentBefore: '[]cd
efabc
[]def
', + contentBefore: 'abc
[]def
abc[]def
', + contentAfter: 'abc[]def
a http://test.com b http://test.com [] c http://test.com d
', }); }); - }) + }); describe('history', () => { describe('undo', () => { @@ -3537,252 +3606,250 @@ X[] describe('ArrowRight', () => { it('should move past a zws (collapsed)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab[]\u200Bcd', + contentBefore: 'ab[]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight'}); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200Bc[]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200Bc[]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab[]\u200Bcd', + contentBefore: 'ab[]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight'}); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200Bc[]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200Bc[]d
' }); }); it('should select a zws', async () => { await testEditor(BasicEditor, { - contentBefore: '[ab]\u200Bcd', + contentBefore: '[ab]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: '[ab\u200B]cd', - // Final state: '[ab\u200Bc]d' + contentAfter: '[ab\u200B]cd
', + // Final state: '[ab\u200Bc]d
' }); await testEditor(BasicEditor, { - contentBefore: '[ab]\u200Bcd', + contentBefore: '[ab]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: '[ab\u200B]cd', - // Final state: '[ab\u200Bc]d' + contentAfter: '[ab\u200B]cd
', + // Final state: '[ab\u200Bc]d
' }); }); it('should select a zws (2)', async () => { await testEditor(BasicEditor, { - contentBefore: 'a[b]\u200Bcd', + contentBefore: 'a[b]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'a[b\u200B]cd', - // Final state: 'a[b\u200Bc]d' + contentAfter: 'a[b\u200B]cd
', + // Final state: 'a[b\u200Bc]d
' }); await testEditor(BasicEditor, { - contentBefore: 'a[b]\u200Bcd', + contentBefore: 'a[b]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'a[b\u200B]cd', - // Final state: 'a[b\u200Bc]d' + contentAfter: 'a[b\u200B]cd
', + // Final state: 'a[b\u200Bc]d
' }); }); it('should select a zws (3)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab[]\u200Bcd', + contentBefore: 'ab[]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab[\u200B]cd', - // Final state: 'ab[\u200Bc]d' + contentAfter: 'ab[\u200B]cd
', + // Final state: 'ab[\u200Bc]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab[]\u200Bcd', + contentBefore: 'ab[]\u200Bcd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab[\u200B]cd', - // Final state: 'ab[\u200Bc]d' + contentAfter: 'ab[\u200B]cd
', + // Final state: 'ab[\u200Bc]d
' }); }); it('should select a zws backwards', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab]\u200B[cd', + contentBefore: 'ab]\u200B[cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200B[c]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200B[c]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab]\u200B[cd', + contentBefore: 'ab]\u200B[cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200B[c]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200B[c]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab]\u200B[cd', + contentBefore: 'ab]\u200B[cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200B[c]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200B[c]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab]\u200B[cd', + contentBefore: 'ab]\u200B[cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B[]cd', - // Final state: 'ab\u200B[c]d' + contentAfter: 'ab\u200B[]cd
', + // Final state: 'ab\u200B[c]d
' }); }); it('should select a zws backwards (2)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab]\u200Bc[d', + contentBefore: 'ab]\u200Bc[d
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B]c[d', - // Final state: 'ab\u200Bc[]d' + contentAfter: 'ab\u200B]c[d
', + // Final state: 'ab\u200Bc[]d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab]\u200Bc[d', + contentBefore: 'ab]\u200Bc[d
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowRight', shiftKey: true }); }, - contentAfter: 'ab\u200B]c[d', - // Final state: 'ab\u200Bc[]d' + contentAfter: 'ab\u200B]c[d
', + // Final state: 'ab\u200Bc[]d
' }); }); }); describe('ArrowLeft', () => { it('should move past a zws (collapsed)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab\u200B[]cd', + contentBefore: 'ab\u200B[]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft'}); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a[]b\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', }); await testEditor(BasicEditor, { - contentBefore: 'ab\u200B[]cd', + contentBefore: 'ab\u200B[]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft'}); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a[]b\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', }); }); it('should select a zws backwards', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab\u200B[]cd', + contentBefore: 'ab\u200B[]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200B[cd', - // Final state: 'a]b\u200B[cd' + contentAfter: 'ab]\u200B[cd
', + // Final state: 'a]b\u200B[cd
' }); await testEditor(BasicEditor, { - contentBefore: 'ab\u200B[]cd', + contentBefore: 'ab\u200B[]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200B[cd', - // Final state: 'a]b\u200B[cd' + contentAfter: 'ab]\u200B[cd
', + // Final state: 'a]b\u200B[cd
' }); }); it('should select a zws backwards (2)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab\u200B]cd[', + contentBefore: 'ab\u200B]cd[
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200Bcd[', - // Final state: 'a]b\u200Bcd[' + contentAfter: 'ab]\u200Bcd[
', + // Final state: 'a]b\u200Bcd[
' }); await testEditor(BasicEditor, { - contentBefore: 'ab\u200B]cd[', + contentBefore: 'ab\u200B]cd[
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200Bcd[', - // Final state: 'a]b\u200Bcd[' + contentAfter: 'ab]\u200Bcd[
', + // Final state: 'a]b\u200Bcd[
' }); }); it('should select a zws backwards (3)', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab\u200B]c[d', + contentBefore: 'ab\u200B]c[d
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200Bc[d', - // Final state: 'a]b\u200Bc[d' + contentAfter: 'ab]\u200Bc[d
', + // Final state: 'a]b\u200Bc[d
' }); await testEditor(BasicEditor, { - contentBefore: 'ab\u200B]c[d', + contentBefore: 'ab\u200B]c[d
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab]\u200Bc[d', - // Final state: 'a]b\u200Bc[d' + contentAfter: 'ab]\u200Bc[d
', + // Final state: 'a]b\u200Bc[d
' }); }); it('should deselect a zws', async () => { await testEditor(BasicEditor, { - contentBefore: 'ab[\u200B]cd', + contentBefore: 'ab[\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a]b[\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', + // Final state: 'a]b[\u200Bcd
' }); await testEditor(BasicEditor, { - contentBefore: 'ab[\u200B]cd', + contentBefore: 'ab[\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a]b[\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', + // Final state: 'a]b[\u200Bcd
' }); await testEditor(BasicEditor, { - contentBefore: 'ab[\u200B]cd', + contentBefore: 'ab[\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a]b[\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', + // Final state: 'a]b[\u200Bcd
' }); await testEditor(BasicEditor, { - contentBefore: 'ab[\u200B]cd', + contentBefore: 'ab[\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'ab[]\u200Bcd', - // Final state: 'a]b[\u200Bcd' + contentAfter: 'ab[]\u200Bcd
', + // Final state: 'a]b[\u200Bcd
' }); }); it('should deselect a zws (2)', async () => { await testEditor(BasicEditor, { - contentBefore: 'a[b\u200B]cd', + contentBefore: 'a[b\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'a[b]\u200Bcd', - // Final state: 'a[]b\u200Bcd' + contentAfter: 'a[b]\u200Bcd
', + // Final state: 'a[]b\u200Bcd
' }); await testEditor(BasicEditor, { - contentBefore: 'a[b\u200B]cd', + contentBefore: 'a[b\u200B]cd
', stepFunction: async editor => { triggerEvent(editor.editable, 'keydown', { key: 'ArrowLeft', shiftKey: true }); }, - contentAfter: 'a[b]\u200Bcd', - // Final state: 'a[]b\u200Bcd' + contentAfter: 'a[b]\u200Bcd
', + // Final state: 'a[]b\u200Bcd
' }); }); }); diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/insertHTML.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/insertHTML.test.js index c82ae3d2b1f..dc382438aec 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/insertHTML.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/insertHTML.test.js @@ -1,6 +1,6 @@ import { BasicEditor, testEditor } from '../utils.js'; -describe('insetHTML', () => { +describe('insert HTML', () => { describe('collapsed selection', () => { it('should insert html in an empty paragraph', async () => { await testEditor(BasicEditor, { @@ -37,55 +37,55 @@ describe('insetHTML', () => { }); it('should insert html in an empty editable', async () => { await testEditor(BasicEditor, { - contentBefore: '[][]
\u200b[]
[]
a[]b
a\u200b[]b
a[]b
a[]e
b
c
d
'); }, - contentAfter: 'abc
d[]eab
c
d[]e
[a]
\u200b[]
[]
a[b]c
a\u200b[]c
a[]c