kempo-ui 0.3.9 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/components/HtmlEditor.js +15 -15
  2. package/docs/src/components/HtmlEditor.js +15 -15
  3. package/package.json +3 -3
  4. package/src/components/HtmlEditor.js +24 -29
  5. package/tests/components/htmlEditorControls/Align.browser-test.js +203 -0
  6. package/tests/components/htmlEditorControls/Bold.browser-test.js +127 -0
  7. package/tests/components/htmlEditorControls/BulletList.browser-test.js +127 -0
  8. package/tests/components/htmlEditorControls/CharacterCount.browser-test.js +106 -0
  9. package/tests/components/htmlEditorControls/ClearFormatting.browser-test.js +127 -0
  10. package/tests/components/htmlEditorControls/CodeBlock.browser-test.js +143 -0
  11. package/tests/components/htmlEditorControls/ControlGroup.browser-test.js +116 -0
  12. package/tests/components/htmlEditorControls/ControlSpacer.browser-test.js +69 -0
  13. package/tests/components/htmlEditorControls/CreateLink.browser-test.js +129 -0
  14. package/tests/components/htmlEditorControls/DropdownControl.browser-test.js +138 -0
  15. package/tests/components/htmlEditorControls/FormatBlock.browser-test.js +163 -0
  16. package/tests/components/htmlEditorControls/InlineCode.browser-test.js +127 -0
  17. package/tests/components/htmlEditorControls/InsertTable.browser-test.js +129 -0
  18. package/tests/components/htmlEditorControls/Italic.browser-test.js +127 -0
  19. package/tests/components/htmlEditorControls/Mode.browser-test.js +138 -0
  20. package/tests/components/htmlEditorControls/NumberList.browser-test.js +127 -0
  21. package/tests/components/htmlEditorControls/Strikethrough.browser-test.js +127 -0
  22. package/tests/components/htmlEditorControls/TextBackgroundColor.browser-test.js +136 -0
  23. package/tests/components/htmlEditorControls/TextColor.browser-test.js +136 -0
  24. package/tests/components/htmlEditorControls/Underline.browser-test.js +127 -0
  25. package/tests/components/htmlEditorControls/WordCount.browser-test.js +106 -0
@@ -0,0 +1,163 @@
1
+ import '../../../src/components/HtmlEditor.js';
2
+ import FormatBlock from '../../../src/components/htmlEditorControls/FormatBlock.js';
3
+
4
+ const createEditorWithControl = async (tag = 'h1') => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `
7
+ <k-html-editor>
8
+ <k-hec-format-block tag="${tag}" slot="toolbar-top-left"></k-hec-format-block>
9
+ </k-html-editor>
10
+ `;
11
+ document.body.appendChild(container);
12
+ const editor = container.querySelector('k-html-editor');
13
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
14
+ const control = container.querySelector('k-hec-format-block');
15
+ return { container, editor, control };
16
+ };
17
+
18
+ const cleanup = container => {
19
+ if(container?.parentNode) container.parentNode.removeChild(container);
20
+ };
21
+
22
+ export default {
23
+ /*
24
+ Element Creation
25
+ */
26
+ 'should create k-hec-format-block element': async ({pass, fail}) => {
27
+ const { container, control } = await createEditorWithControl();
28
+ if(!(control instanceof FormatBlock)){
29
+ cleanup(container);
30
+ return fail('Element should be instance of FormatBlock');
31
+ }
32
+ cleanup(container);
33
+ pass('FormatBlock element created correctly');
34
+ },
35
+
36
+ 'should have shadow root': async ({pass, fail}) => {
37
+ const { container, control } = await createEditorWithControl();
38
+ if(!control.shadowRoot){
39
+ cleanup(container);
40
+ return fail('FormatBlock should have shadow root');
41
+ }
42
+ cleanup(container);
43
+ pass('FormatBlock has shadow root');
44
+ },
45
+
46
+ 'should render a button': async ({pass, fail}) => {
47
+ const { container, control } = await createEditorWithControl();
48
+ await control.updateComplete;
49
+ const button = control.shadowRoot.querySelector('button');
50
+ if(!button){
51
+ cleanup(container);
52
+ return fail('FormatBlock should render a button');
53
+ }
54
+ cleanup(container);
55
+ pass('FormatBlock renders a button');
56
+ },
57
+
58
+ /*
59
+ Tag Property
60
+ */
61
+ 'should default tag to "p"': async ({pass, fail}) => {
62
+ const container = document.createElement('div');
63
+ container.innerHTML = `
64
+ <k-html-editor>
65
+ <k-hec-format-block slot="toolbar-top-left"></k-hec-format-block>
66
+ </k-html-editor>
67
+ `;
68
+ document.body.appendChild(container);
69
+ const editor = container.querySelector('k-html-editor');
70
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
71
+ const control = container.querySelector('k-hec-format-block');
72
+ if(control.tag !== 'p'){
73
+ cleanup(container);
74
+ return fail(`Expected default tag "p", got "${control.tag}"`);
75
+ }
76
+ cleanup(container);
77
+ pass('Default tag is "p"');
78
+ },
79
+
80
+ 'should accept tag attribute': async ({pass, fail}) => {
81
+ const { container, control } = await createEditorWithControl('h2');
82
+ if(control.tag !== 'h2'){
83
+ cleanup(container);
84
+ return fail(`Expected tag "h2", got "${control.tag}"`);
85
+ }
86
+ cleanup(container);
87
+ pass('Tag attribute is accepted');
88
+ },
89
+
90
+ /*
91
+ Editor Integration
92
+ */
93
+ 'should find parent editor': async ({pass, fail}) => {
94
+ const { container, editor, control } = await createEditorWithControl();
95
+ if(control.editor !== editor){
96
+ cleanup(container);
97
+ return fail('FormatBlock should find its parent k-html-editor');
98
+ }
99
+ cleanup(container);
100
+ pass('FormatBlock finds parent editor');
101
+ },
102
+
103
+ 'click should call editor.formatBlock with tag value': async ({pass, fail}) => {
104
+ const { container, editor, control } = await createEditorWithControl('h3');
105
+ await control.updateComplete;
106
+ let calledWith = null;
107
+ editor.formatBlock = tag => { calledWith = tag; };
108
+ control.shadowRoot.querySelector('button').click();
109
+ if(calledWith !== 'h3'){
110
+ cleanup(container);
111
+ return fail(`Expected formatBlock("h3"), got formatBlock("${calledWith}")`);
112
+ }
113
+ cleanup(container);
114
+ pass('Click calls editor.formatBlock with tag value');
115
+ },
116
+
117
+ /*
118
+ Helper Methods
119
+ */
120
+ 'getDefaultLabel should return correct labels': async ({pass, fail}) => {
121
+ const { container, control } = await createEditorWithControl('h1');
122
+ const expected = {h1: 'Heading 1', h2: 'Heading 2', p: 'Paragraph'};
123
+ for(const [tag, label] of Object.entries(expected)){
124
+ control.tag = tag;
125
+ if(control.getDefaultLabel() !== label){
126
+ cleanup(container);
127
+ return fail(`Expected label "${label}" for tag "${tag}", got "${control.getDefaultLabel()}"`);
128
+ }
129
+ }
130
+ cleanup(container);
131
+ pass('getDefaultLabel returns correct labels');
132
+ },
133
+
134
+ 'getDefaultIcon should return correct icons': async ({pass, fail}) => {
135
+ const { container, control } = await createEditorWithControl('h1');
136
+ const expected = {h1: 'format_h1', h2: 'format_h2', p: 'format_paragraph'};
137
+ for(const [tag, icon] of Object.entries(expected)){
138
+ control.tag = tag;
139
+ if(control.getDefaultIcon() !== icon){
140
+ cleanup(container);
141
+ return fail(`Expected icon "${icon}" for tag "${tag}", got "${control.getDefaultIcon()}"`);
142
+ }
143
+ }
144
+ cleanup(container);
145
+ pass('getDefaultIcon returns correct icons');
146
+ },
147
+
148
+ /*
149
+ Code Mode Visibility
150
+ */
151
+ 'should hide in code mode': async ({pass, fail}) => {
152
+ const { container, editor, control } = await createEditorWithControl();
153
+ editor.setMode('code');
154
+ await new Promise(r => editor.addEventListener('mode-changed', r, { once: true }));
155
+ await control.updateComplete;
156
+ if(!control.hidden){
157
+ cleanup(container);
158
+ return fail('FormatBlock should be hidden in code mode');
159
+ }
160
+ cleanup(container);
161
+ pass('FormatBlock is hidden in code mode');
162
+ },
163
+ };
@@ -0,0 +1,127 @@
1
+ import '../../../src/components/HtmlEditor.js';
2
+ import InlineCode from '../../../src/components/htmlEditorControls/InlineCode.js';
3
+
4
+ const createEditorWithControl = async () => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `
7
+ <k-html-editor>
8
+ <k-hec-inline-code slot="toolbar-top-left"></k-hec-inline-code>
9
+ </k-html-editor>
10
+ `;
11
+ document.body.appendChild(container);
12
+ const editor = container.querySelector('k-html-editor');
13
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
14
+ const control = container.querySelector('k-hec-inline-code');
15
+ return { container, editor, control };
16
+ };
17
+
18
+ const cleanup = container => {
19
+ if(container?.parentNode) container.parentNode.removeChild(container);
20
+ };
21
+
22
+ export default {
23
+ /*
24
+ Element Creation
25
+ */
26
+ 'should create k-hec-inline-code element': async ({pass, fail}) => {
27
+ const { container, control } = await createEditorWithControl();
28
+ if(!(control instanceof InlineCode)){
29
+ cleanup(container);
30
+ return fail('Element should be instance of InlineCode');
31
+ }
32
+ cleanup(container);
33
+ pass('InlineCode element created correctly');
34
+ },
35
+
36
+ 'should have shadow root': async ({pass, fail}) => {
37
+ const { container, control } = await createEditorWithControl();
38
+ if(!control.shadowRoot){
39
+ cleanup(container);
40
+ return fail('InlineCode should have shadow root');
41
+ }
42
+ cleanup(container);
43
+ pass('InlineCode has shadow root');
44
+ },
45
+
46
+ 'should render a button': async ({pass, fail}) => {
47
+ const { container, control } = await createEditorWithControl();
48
+ await control.updateComplete;
49
+ const button = control.shadowRoot.querySelector('button');
50
+ if(!button){
51
+ cleanup(container);
52
+ return fail('InlineCode should render a button');
53
+ }
54
+ cleanup(container);
55
+ pass('InlineCode renders a button');
56
+ },
57
+
58
+ 'should render code_blocks icon': async ({pass, fail}) => {
59
+ const { container, control } = await createEditorWithControl();
60
+ await control.updateComplete;
61
+ const icon = control.shadowRoot.querySelector('k-icon');
62
+ if(!icon){
63
+ cleanup(container);
64
+ return fail('InlineCode should render a k-icon');
65
+ }
66
+ if(icon.getAttribute('name') !== 'code_blocks'){
67
+ cleanup(container);
68
+ return fail(`Expected icon name "code_blocks", got "${icon.getAttribute('name')}"`);
69
+ }
70
+ cleanup(container);
71
+ pass('InlineCode renders code_blocks icon');
72
+ },
73
+
74
+ /*
75
+ Editor Integration
76
+ */
77
+ 'should find parent editor': async ({pass, fail}) => {
78
+ const { container, editor, control } = await createEditorWithControl();
79
+ if(control.editor !== editor){
80
+ cleanup(container);
81
+ return fail('InlineCode should find its parent k-html-editor');
82
+ }
83
+ cleanup(container);
84
+ pass('InlineCode finds parent editor');
85
+ },
86
+
87
+ 'click should call editor.inlineCode()': async ({pass, fail}) => {
88
+ const { container, editor, control } = await createEditorWithControl();
89
+ await control.updateComplete;
90
+ let called = false;
91
+ editor.inlineCode = () => { called = true; };
92
+ control.shadowRoot.querySelector('button').click();
93
+ if(!called){
94
+ cleanup(container);
95
+ return fail('Click should call editor.inlineCode()');
96
+ }
97
+ cleanup(container);
98
+ pass('Click calls editor.inlineCode()');
99
+ },
100
+
101
+ /*
102
+ Code Mode Visibility
103
+ */
104
+ 'should hide in code mode': async ({pass, fail}) => {
105
+ const { container, editor, control } = await createEditorWithControl();
106
+ editor.setMode('code');
107
+ await new Promise(r => editor.addEventListener('mode-changed', r, { once: true }));
108
+ await control.updateComplete;
109
+ if(!control.hidden){
110
+ cleanup(container);
111
+ return fail('InlineCode should be hidden in code mode');
112
+ }
113
+ cleanup(container);
114
+ pass('InlineCode is hidden in code mode');
115
+ },
116
+
117
+ 'should be visible in visual mode': async ({pass, fail}) => {
118
+ const { container, editor, control } = await createEditorWithControl();
119
+ await control.updateComplete;
120
+ if(control.hidden){
121
+ cleanup(container);
122
+ return fail('InlineCode should be visible in visual mode');
123
+ }
124
+ cleanup(container);
125
+ pass('InlineCode is visible in visual mode');
126
+ },
127
+ };
@@ -0,0 +1,129 @@
1
+ import '../../../src/components/HtmlEditor.js';
2
+ import InsertTable from '../../../src/components/htmlEditorControls/InsertTable.js';
3
+
4
+ const createEditorWithControl = async () => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `
7
+ <k-html-editor>
8
+ <k-hec-insert-table slot="toolbar-top-left"></k-hec-insert-table>
9
+ </k-html-editor>
10
+ `;
11
+ document.body.appendChild(container);
12
+ const editor = container.querySelector('k-html-editor');
13
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
14
+ const control = container.querySelector('k-hec-insert-table');
15
+ return { container, editor, control };
16
+ };
17
+
18
+ const cleanup = container => {
19
+ if(container?.parentNode) container.parentNode.removeChild(container);
20
+ };
21
+
22
+ export default {
23
+ /*
24
+ Element Creation
25
+ */
26
+ 'should create k-hec-insert-table element': async ({pass, fail}) => {
27
+ const { container, control } = await createEditorWithControl();
28
+ if(!(control instanceof InsertTable)){
29
+ cleanup(container);
30
+ return fail('Element should be instance of InsertTable');
31
+ }
32
+ cleanup(container);
33
+ pass('InsertTable element created correctly');
34
+ },
35
+
36
+ 'should have shadow root': async ({pass, fail}) => {
37
+ const { container, control } = await createEditorWithControl();
38
+ if(!control.shadowRoot){
39
+ cleanup(container);
40
+ return fail('InsertTable should have shadow root');
41
+ }
42
+ cleanup(container);
43
+ pass('InsertTable has shadow root');
44
+ },
45
+
46
+ 'should render a button': async ({pass, fail}) => {
47
+ const { container, control } = await createEditorWithControl();
48
+ await control.updateComplete;
49
+ const button = control.shadowRoot.querySelector('button');
50
+ if(!button){
51
+ cleanup(container);
52
+ return fail('InsertTable should render a button');
53
+ }
54
+ cleanup(container);
55
+ pass('InsertTable renders a button');
56
+ },
57
+
58
+ 'should render table icon': async ({pass, fail}) => {
59
+ const { container, control } = await createEditorWithControl();
60
+ await control.updateComplete;
61
+ const icon = control.shadowRoot.querySelector('k-icon');
62
+ if(!icon){
63
+ cleanup(container);
64
+ return fail('InsertTable should render a k-icon');
65
+ }
66
+ if(icon.getAttribute('name') !== 'table'){
67
+ cleanup(container);
68
+ return fail(`Expected icon name "table", got "${icon.getAttribute('name')}"`);
69
+ }
70
+ cleanup(container);
71
+ pass('InsertTable renders table icon');
72
+ },
73
+
74
+ /*
75
+ Editor Integration
76
+ */
77
+ 'should find parent editor': async ({pass, fail}) => {
78
+ const { container, editor, control } = await createEditorWithControl();
79
+ if(control.editor !== editor){
80
+ cleanup(container);
81
+ return fail('InsertTable should find its parent k-html-editor');
82
+ }
83
+ cleanup(container);
84
+ pass('InsertTable finds parent editor');
85
+ },
86
+
87
+ 'click should open a dialog': async ({pass, fail}) => {
88
+ const { container, editor, control } = await createEditorWithControl();
89
+ await control.updateComplete;
90
+ editor.getTableAtSelection = () => null;
91
+ control.shadowRoot.querySelector('button').click();
92
+ await new Promise(r => setTimeout(r, 100));
93
+ const dialog = document.querySelector('k-dialog');
94
+ if(!dialog){
95
+ cleanup(container);
96
+ return fail('Click should open a k-dialog');
97
+ }
98
+ dialog.remove();
99
+ cleanup(container);
100
+ pass('Click opens a dialog');
101
+ },
102
+
103
+ /*
104
+ Code Mode Visibility
105
+ */
106
+ 'should hide in code mode': async ({pass, fail}) => {
107
+ const { container, editor, control } = await createEditorWithControl();
108
+ editor.setMode('code');
109
+ await new Promise(r => editor.addEventListener('mode-changed', r, { once: true }));
110
+ await control.updateComplete;
111
+ if(!control.hidden){
112
+ cleanup(container);
113
+ return fail('InsertTable should be hidden in code mode');
114
+ }
115
+ cleanup(container);
116
+ pass('InsertTable is hidden in code mode');
117
+ },
118
+
119
+ 'should be visible in visual mode': async ({pass, fail}) => {
120
+ const { container, editor, control } = await createEditorWithControl();
121
+ await control.updateComplete;
122
+ if(control.hidden){
123
+ cleanup(container);
124
+ return fail('InsertTable should be visible in visual mode');
125
+ }
126
+ cleanup(container);
127
+ pass('InsertTable is visible in visual mode');
128
+ },
129
+ };
@@ -0,0 +1,127 @@
1
+ import '../../../src/components/HtmlEditor.js';
2
+ import Italic from '../../../src/components/htmlEditorControls/Italic.js';
3
+
4
+ const createEditorWithControl = async () => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `
7
+ <k-html-editor>
8
+ <k-hec-italic slot="toolbar-top-left"></k-hec-italic>
9
+ </k-html-editor>
10
+ `;
11
+ document.body.appendChild(container);
12
+ const editor = container.querySelector('k-html-editor');
13
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
14
+ const control = container.querySelector('k-hec-italic');
15
+ return { container, editor, control };
16
+ };
17
+
18
+ const cleanup = container => {
19
+ if(container?.parentNode) container.parentNode.removeChild(container);
20
+ };
21
+
22
+ export default {
23
+ /*
24
+ Element Creation
25
+ */
26
+ 'should create k-hec-italic element': async ({pass, fail}) => {
27
+ const { container, control } = await createEditorWithControl();
28
+ if(!(control instanceof Italic)){
29
+ cleanup(container);
30
+ return fail('Element should be instance of Italic');
31
+ }
32
+ cleanup(container);
33
+ pass('Italic element created correctly');
34
+ },
35
+
36
+ 'should have shadow root': async ({pass, fail}) => {
37
+ const { container, control } = await createEditorWithControl();
38
+ if(!control.shadowRoot){
39
+ cleanup(container);
40
+ return fail('Italic should have shadow root');
41
+ }
42
+ cleanup(container);
43
+ pass('Italic has shadow root');
44
+ },
45
+
46
+ 'should render a button': async ({pass, fail}) => {
47
+ const { container, control } = await createEditorWithControl();
48
+ await control.updateComplete;
49
+ const button = control.shadowRoot.querySelector('button');
50
+ if(!button){
51
+ cleanup(container);
52
+ return fail('Italic should render a button');
53
+ }
54
+ cleanup(container);
55
+ pass('Italic renders a button');
56
+ },
57
+
58
+ 'should render format_italic icon': async ({pass, fail}) => {
59
+ const { container, control } = await createEditorWithControl();
60
+ await control.updateComplete;
61
+ const icon = control.shadowRoot.querySelector('k-icon');
62
+ if(!icon){
63
+ cleanup(container);
64
+ return fail('Italic should render a k-icon');
65
+ }
66
+ if(icon.getAttribute('name') !== 'format_italic'){
67
+ cleanup(container);
68
+ return fail(`Expected icon name "format_italic", got "${icon.getAttribute('name')}"`);
69
+ }
70
+ cleanup(container);
71
+ pass('Italic renders format_italic icon');
72
+ },
73
+
74
+ /*
75
+ Editor Integration
76
+ */
77
+ 'should find parent editor': async ({pass, fail}) => {
78
+ const { container, editor, control } = await createEditorWithControl();
79
+ if(control.editor !== editor){
80
+ cleanup(container);
81
+ return fail('Italic should find its parent k-html-editor');
82
+ }
83
+ cleanup(container);
84
+ pass('Italic finds parent editor');
85
+ },
86
+
87
+ 'click should call editor.italic()': async ({pass, fail}) => {
88
+ const { container, editor, control } = await createEditorWithControl();
89
+ await control.updateComplete;
90
+ let called = false;
91
+ editor.italic = () => { called = true; };
92
+ control.shadowRoot.querySelector('button').click();
93
+ if(!called){
94
+ cleanup(container);
95
+ return fail('Click should call editor.italic()');
96
+ }
97
+ cleanup(container);
98
+ pass('Click calls editor.italic()');
99
+ },
100
+
101
+ /*
102
+ Code Mode Visibility
103
+ */
104
+ 'should hide in code mode': async ({pass, fail}) => {
105
+ const { container, editor, control } = await createEditorWithControl();
106
+ editor.setMode('code');
107
+ await new Promise(r => editor.addEventListener('mode-changed', r, { once: true }));
108
+ await control.updateComplete;
109
+ if(!control.hidden){
110
+ cleanup(container);
111
+ return fail('Italic should be hidden in code mode');
112
+ }
113
+ cleanup(container);
114
+ pass('Italic is hidden in code mode');
115
+ },
116
+
117
+ 'should be visible in visual mode': async ({pass, fail}) => {
118
+ const { container, editor, control } = await createEditorWithControl();
119
+ await control.updateComplete;
120
+ if(control.hidden){
121
+ cleanup(container);
122
+ return fail('Italic should be visible in visual mode');
123
+ }
124
+ cleanup(container);
125
+ pass('Italic is visible in visual mode');
126
+ },
127
+ };
@@ -0,0 +1,138 @@
1
+ import '../../../src/components/HtmlEditor.js';
2
+ import Mode from '../../../src/components/htmlEditorControls/Mode.js';
3
+
4
+ const createEditorWithControl = async () => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `
7
+ <k-html-editor>
8
+ <k-hec-mode slot="toolbar-top-right"></k-hec-mode>
9
+ </k-html-editor>
10
+ `;
11
+ document.body.appendChild(container);
12
+ const editor = container.querySelector('k-html-editor');
13
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
14
+ const control = container.querySelector('k-hec-mode');
15
+ return { container, editor, control };
16
+ };
17
+
18
+ const cleanup = container => {
19
+ if(container?.parentNode) container.parentNode.removeChild(container);
20
+ };
21
+
22
+ export default {
23
+ /*
24
+ Element Creation
25
+ */
26
+ 'should create k-hec-mode element': async ({pass, fail}) => {
27
+ const { container, control } = await createEditorWithControl();
28
+ if(!(control instanceof Mode)){
29
+ cleanup(container);
30
+ return fail('Element should be instance of Mode');
31
+ }
32
+ cleanup(container);
33
+ pass('Mode element created correctly');
34
+ },
35
+
36
+ 'should have shadow root': async ({pass, fail}) => {
37
+ const { container, control } = await createEditorWithControl();
38
+ if(!control.shadowRoot){
39
+ cleanup(container);
40
+ return fail('Mode should have shadow root');
41
+ }
42
+ cleanup(container);
43
+ pass('Mode has shadow root');
44
+ },
45
+
46
+ 'should render a button': async ({pass, fail}) => {
47
+ const { container, control } = await createEditorWithControl();
48
+ await control.updateComplete;
49
+ const button = control.shadowRoot.querySelector('button');
50
+ if(!button){
51
+ cleanup(container);
52
+ return fail('Mode should render a button');
53
+ }
54
+ cleanup(container);
55
+ pass('Mode renders a button');
56
+ },
57
+
58
+ 'should render code icon': async ({pass, fail}) => {
59
+ const { container, control } = await createEditorWithControl();
60
+ await control.updateComplete;
61
+ const icon = control.shadowRoot.querySelector('k-icon');
62
+ if(!icon){
63
+ cleanup(container);
64
+ return fail('Mode should render a k-icon');
65
+ }
66
+ if(icon.getAttribute('name') !== 'code'){
67
+ cleanup(container);
68
+ return fail(`Expected icon name "code", got "${icon.getAttribute('name')}"`);
69
+ }
70
+ cleanup(container);
71
+ pass('Mode renders code icon');
72
+ },
73
+
74
+ /*
75
+ Editor Integration
76
+ */
77
+ 'should find parent editor': async ({pass, fail}) => {
78
+ const { container, editor, control } = await createEditorWithControl();
79
+ if(control.editor !== editor){
80
+ cleanup(container);
81
+ return fail('Mode should find its parent k-html-editor');
82
+ }
83
+ cleanup(container);
84
+ pass('Mode finds parent editor');
85
+ },
86
+
87
+ 'click should call editor.toggleMode()': async ({pass, fail}) => {
88
+ const { container, editor, control } = await createEditorWithControl();
89
+ await control.updateComplete;
90
+ let called = false;
91
+ editor.toggleMode = () => { called = true; };
92
+ control.shadowRoot.querySelector('button').click();
93
+ if(!called){
94
+ cleanup(container);
95
+ return fail('Click should call editor.toggleMode()');
96
+ }
97
+ cleanup(container);
98
+ pass('Click calls editor.toggleMode()');
99
+ },
100
+
101
+ /*
102
+ Mode Visibility
103
+ */
104
+ 'should NOT hide in code mode': async ({pass, fail}) => {
105
+ const { container, editor, control } = await createEditorWithControl();
106
+ editor.setMode('code');
107
+ await new Promise(r => editor.addEventListener('mode-changed', r, { once: true }));
108
+ await control.updateComplete;
109
+ if(control.hidden){
110
+ cleanup(container);
111
+ return fail('Mode should NOT be hidden in code mode');
112
+ }
113
+ cleanup(container);
114
+ pass('Mode is visible in code mode');
115
+ },
116
+
117
+ 'should be visible in visual mode': async ({pass, fail}) => {
118
+ const { container, control } = await createEditorWithControl();
119
+ await control.updateComplete;
120
+ if(control.hidden){
121
+ cleanup(container);
122
+ return fail('Mode should be visible in visual mode');
123
+ }
124
+ cleanup(container);
125
+ pass('Mode is visible in visual mode');
126
+ },
127
+
128
+ 'should track editor mode': async ({pass, fail}) => {
129
+ const { container, editor, control } = await createEditorWithControl();
130
+ await control.updateComplete;
131
+ if(control.mode !== 'visual'){
132
+ cleanup(container);
133
+ return fail(`Expected initial mode "visual", got "${control.mode}"`);
134
+ }
135
+ cleanup(container);
136
+ pass('Mode tracks editor mode');
137
+ },
138
+ };