@opendata-ai/openchart-vue 2.0.0

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.
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Tests for VizThemeProvider, useVizTheme, and useVizDarkMode.
3
+ *
4
+ * Verifies that themes and dark mode preferences cascade through the
5
+ * provider hierarchy and that nested providers override parent values.
6
+ */
7
+
8
+ import type { ThemeConfig } from '@opendata-ai/openchart-core';
9
+ import { flushPromises, mount } from '@vue/test-utils';
10
+ import { describe, expect, it } from 'vitest';
11
+ import { defineComponent, h } from 'vue';
12
+ import { useVizDarkMode, useVizTheme } from '../context';
13
+ import { VizThemeProvider } from '../ThemeProvider';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Test harness components
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const ThemeConsumer = defineComponent({
20
+ props: {
21
+ testId: {
22
+ type: String,
23
+ default: 'theme-output',
24
+ },
25
+ },
26
+ setup(props) {
27
+ const theme = useVizTheme();
28
+ return () =>
29
+ h(
30
+ 'div',
31
+ { 'data-testid': props.testId },
32
+ theme.value ? JSON.stringify(theme.value) : 'no-theme',
33
+ );
34
+ },
35
+ });
36
+
37
+ const DarkModeConsumer = defineComponent({
38
+ props: {
39
+ testId: {
40
+ type: String,
41
+ default: 'darkmode-output',
42
+ },
43
+ },
44
+ setup(props) {
45
+ const darkMode = useVizDarkMode();
46
+ return () => h('div', { 'data-testid': props.testId }, darkMode.value ?? 'undefined');
47
+ },
48
+ });
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Helpers
52
+ // ---------------------------------------------------------------------------
53
+
54
+ function findByTestId(wrapper: ReturnType<typeof mount>, testId: string) {
55
+ return wrapper.find(`[data-testid="${testId}"]`);
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // useVizTheme
60
+ // ---------------------------------------------------------------------------
61
+
62
+ describe('useVizTheme', () => {
63
+ it('returns undefined when used outside of a provider', async () => {
64
+ const wrapper = mount(ThemeConsumer);
65
+ await flushPromises();
66
+
67
+ const output = findByTestId(wrapper, 'theme-output');
68
+ expect(output.text()).toBe('no-theme');
69
+ wrapper.unmount();
70
+ });
71
+ });
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // VizThemeProvider
75
+ // ---------------------------------------------------------------------------
76
+
77
+ describe('VizThemeProvider', () => {
78
+ it('provides theme to child components', async () => {
79
+ const theme: ThemeConfig = {
80
+ colors: { categorical: ['#ff0000', '#00ff00', '#0000ff'] },
81
+ };
82
+
83
+ const wrapper = mount(VizThemeProvider, {
84
+ props: { theme },
85
+ slots: { default: () => h(ThemeConsumer) },
86
+ });
87
+ await flushPromises();
88
+
89
+ const output = findByTestId(wrapper, 'theme-output');
90
+ const parsed = JSON.parse(output.text());
91
+ expect(parsed.colors.categorical).toEqual(['#ff0000', '#00ff00', '#0000ff']);
92
+ wrapper.unmount();
93
+ });
94
+
95
+ it('provides undefined theme when passed undefined', async () => {
96
+ const wrapper = mount(VizThemeProvider, {
97
+ props: { theme: undefined },
98
+ slots: { default: () => h(ThemeConsumer) },
99
+ });
100
+ await flushPromises();
101
+
102
+ const output = findByTestId(wrapper, 'theme-output');
103
+ expect(output.text()).toBe('no-theme');
104
+ wrapper.unmount();
105
+ });
106
+
107
+ it('nested providers override parent theme', async () => {
108
+ const parentTheme: ThemeConfig = {
109
+ colors: { categorical: ['#111'] },
110
+ fonts: { family: 'Arial' },
111
+ };
112
+
113
+ const childTheme: ThemeConfig = {
114
+ colors: { categorical: ['#222'] },
115
+ borderRadius: 8,
116
+ };
117
+
118
+ const TestApp = defineComponent({
119
+ setup() {
120
+ return () =>
121
+ h(VizThemeProvider, { theme: parentTheme }, () =>
122
+ h(VizThemeProvider, { theme: childTheme }, () => h(ThemeConsumer)),
123
+ );
124
+ },
125
+ });
126
+
127
+ const wrapper = mount(TestApp);
128
+ await flushPromises();
129
+
130
+ const output = findByTestId(wrapper, 'theme-output');
131
+ const parsed = JSON.parse(output.text());
132
+
133
+ // Inner provider completely replaces the context value (no merging)
134
+ expect(parsed.colors.categorical).toEqual(['#222']);
135
+ expect(parsed.borderRadius).toBe(8);
136
+ // Parent-only fields are absent since inner provider replaces the value
137
+ expect(parsed.fonts).toBeUndefined();
138
+ wrapper.unmount();
139
+ });
140
+
141
+ it('multiple consumers at different nesting levels receive correct themes', async () => {
142
+ const outerTheme: ThemeConfig = {
143
+ colors: { background: '#fff' },
144
+ };
145
+
146
+ const innerTheme: ThemeConfig = {
147
+ colors: { background: '#000' },
148
+ };
149
+
150
+ const TestApp = defineComponent({
151
+ setup() {
152
+ return () =>
153
+ h(VizThemeProvider, { theme: outerTheme }, () => [
154
+ h(ThemeConsumer, { testId: 'outer-theme' }),
155
+ h(VizThemeProvider, { theme: innerTheme }, () =>
156
+ h(ThemeConsumer, { testId: 'inner-theme' }),
157
+ ),
158
+ ]);
159
+ },
160
+ });
161
+
162
+ const wrapper = mount(TestApp);
163
+ await flushPromises();
164
+
165
+ const outerParsed = JSON.parse(findByTestId(wrapper, 'outer-theme').text());
166
+ const innerParsed = JSON.parse(findByTestId(wrapper, 'inner-theme').text());
167
+
168
+ expect(outerParsed.colors.background).toBe('#fff');
169
+ expect(innerParsed.colors.background).toBe('#000');
170
+ wrapper.unmount();
171
+ });
172
+
173
+ it('theme updates propagate to consumers', async () => {
174
+ const theme1: ThemeConfig = { borderRadius: 4 };
175
+ const theme2: ThemeConfig = { borderRadius: 12 };
176
+
177
+ const wrapper = mount(VizThemeProvider, {
178
+ props: { theme: theme1 },
179
+ slots: { default: () => h(ThemeConsumer) },
180
+ });
181
+ await flushPromises();
182
+
183
+ const parsed1 = JSON.parse(findByTestId(wrapper, 'theme-output').text());
184
+ expect(parsed1.borderRadius).toBe(4);
185
+
186
+ await wrapper.setProps({ theme: theme2 });
187
+ await flushPromises();
188
+
189
+ const parsed2 = JSON.parse(findByTestId(wrapper, 'theme-output').text());
190
+ expect(parsed2.borderRadius).toBe(12);
191
+ wrapper.unmount();
192
+ });
193
+ });
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // useVizDarkMode
197
+ // ---------------------------------------------------------------------------
198
+
199
+ describe('useVizDarkMode', () => {
200
+ it('returns undefined when used outside of a provider', async () => {
201
+ const wrapper = mount(DarkModeConsumer);
202
+ await flushPromises();
203
+
204
+ const output = findByTestId(wrapper, 'darkmode-output');
205
+ expect(output.text()).toBe('undefined');
206
+ wrapper.unmount();
207
+ });
208
+
209
+ it('returns the darkMode value from provider', async () => {
210
+ const wrapper = mount(VizThemeProvider, {
211
+ props: { theme: undefined, darkMode: 'force' },
212
+ slots: { default: () => h(DarkModeConsumer) },
213
+ });
214
+ await flushPromises();
215
+
216
+ const output = findByTestId(wrapper, 'darkmode-output');
217
+ expect(output.text()).toBe('force');
218
+ wrapper.unmount();
219
+ });
220
+
221
+ it('returns undefined when provider omits darkMode', async () => {
222
+ const wrapper = mount(VizThemeProvider, {
223
+ props: { theme: undefined },
224
+ slots: { default: () => h(DarkModeConsumer) },
225
+ });
226
+ await flushPromises();
227
+
228
+ const output = findByTestId(wrapper, 'darkmode-output');
229
+ expect(output.text()).toBe('undefined');
230
+ wrapper.unmount();
231
+ });
232
+
233
+ it('nested provider overrides parent darkMode', async () => {
234
+ const TestApp = defineComponent({
235
+ setup() {
236
+ return () =>
237
+ h(VizThemeProvider, { theme: undefined, darkMode: 'force' }, () =>
238
+ h(VizThemeProvider, { theme: undefined, darkMode: 'off' }, () => h(DarkModeConsumer)),
239
+ );
240
+ },
241
+ });
242
+
243
+ const wrapper = mount(TestApp);
244
+ await flushPromises();
245
+
246
+ const output = findByTestId(wrapper, 'darkmode-output');
247
+ expect(output.text()).toBe('off');
248
+ wrapper.unmount();
249
+ });
250
+
251
+ it('darkMode updates propagate to consumers', async () => {
252
+ const wrapper = mount(VizThemeProvider, {
253
+ props: { theme: undefined, darkMode: 'off' },
254
+ slots: { default: () => h(DarkModeConsumer) },
255
+ });
256
+ await flushPromises();
257
+
258
+ expect(findByTestId(wrapper, 'darkmode-output').text()).toBe('off');
259
+
260
+ await wrapper.setProps({ darkMode: 'force' });
261
+ await flushPromises();
262
+
263
+ expect(findByTestId(wrapper, 'darkmode-output').text()).toBe('force');
264
+ wrapper.unmount();
265
+ });
266
+ });
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Tests for useDarkMode composable.
3
+ *
4
+ * Uses thin wrapper components that expose composable state via the DOM,
5
+ * then asserts using mount + flushPromises.
6
+ */
7
+
8
+ import { flushPromises, mount } from '@vue/test-utils';
9
+ import { describe, expect, it, vi } from 'vitest';
10
+ import { defineComponent, h, ref } from 'vue';
11
+ import { useDarkMode } from '../composables/useDarkMode';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // useDarkMode
15
+ // ---------------------------------------------------------------------------
16
+
17
+ const DarkModeHarness = defineComponent({
18
+ props: {
19
+ mode: {
20
+ type: String,
21
+ default: undefined,
22
+ },
23
+ },
24
+ setup(props) {
25
+ const modeRef = ref(props.mode as 'auto' | 'force' | 'off' | undefined);
26
+
27
+ // Watch for prop changes (Vue test-utils setProps updates props but not our ref)
28
+ // We need to use a computed or watchEffect to keep in sync
29
+ const isDark = useDarkMode(modeRef);
30
+
31
+ return { isDark, modeRef };
32
+ },
33
+ render() {
34
+ return h('div', { 'data-testid': 'dark-mode' }, String(this.isDark));
35
+ },
36
+ });
37
+
38
+ // A version of the harness that allows mode changes via exposed methods
39
+ const DarkModeInteractiveHarness = defineComponent({
40
+ props: {
41
+ initialMode: {
42
+ type: String,
43
+ default: undefined,
44
+ },
45
+ },
46
+ setup(props) {
47
+ const modeRef = ref(props.initialMode as 'auto' | 'force' | 'off' | undefined);
48
+ const isDark = useDarkMode(modeRef);
49
+
50
+ return { isDark, modeRef };
51
+ },
52
+ render() {
53
+ return h('div', [
54
+ h('div', { 'data-testid': 'dark-mode' }, String(this.isDark)),
55
+ h(
56
+ 'button',
57
+ {
58
+ 'data-testid': 'set-force',
59
+ onClick: () => {
60
+ this.modeRef = 'force';
61
+ },
62
+ },
63
+ 'Force',
64
+ ),
65
+ h(
66
+ 'button',
67
+ {
68
+ 'data-testid': 'set-off',
69
+ onClick: () => {
70
+ this.modeRef = 'off';
71
+ },
72
+ },
73
+ 'Off',
74
+ ),
75
+ ]);
76
+ },
77
+ });
78
+
79
+ describe('useDarkMode', () => {
80
+ it('returns false when no mode is provided', async () => {
81
+ const wrapper = mount(DarkModeHarness);
82
+ await flushPromises();
83
+
84
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('false');
85
+ wrapper.unmount();
86
+ });
87
+
88
+ it('returns false for "off" mode', async () => {
89
+ const wrapper = mount(DarkModeHarness, { props: { mode: 'off' } });
90
+ await flushPromises();
91
+
92
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('false');
93
+ wrapper.unmount();
94
+ });
95
+
96
+ it('returns true for "force" mode', async () => {
97
+ const wrapper = mount(DarkModeHarness, { props: { mode: 'force' } });
98
+ await flushPromises();
99
+
100
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('true');
101
+ wrapper.unmount();
102
+ });
103
+
104
+ it('reflects system preference for "auto" mode when dark', async () => {
105
+ const spy = vi.spyOn(window, 'matchMedia').mockImplementation(
106
+ (query: string) =>
107
+ ({
108
+ matches: query === '(prefers-color-scheme: dark)',
109
+ media: query,
110
+ addEventListener: vi.fn(),
111
+ removeEventListener: vi.fn(),
112
+ }) as unknown as MediaQueryList,
113
+ );
114
+
115
+ const wrapper = mount(DarkModeHarness, { props: { mode: 'auto' } });
116
+ await flushPromises();
117
+
118
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('true');
119
+
120
+ spy.mockRestore();
121
+ wrapper.unmount();
122
+ });
123
+
124
+ it('reflects system preference for "auto" mode when light', async () => {
125
+ const spy = vi.spyOn(window, 'matchMedia').mockImplementation(
126
+ (query: string) =>
127
+ ({
128
+ matches: false,
129
+ media: query,
130
+ addEventListener: vi.fn(),
131
+ removeEventListener: vi.fn(),
132
+ }) as unknown as MediaQueryList,
133
+ );
134
+
135
+ const wrapper = mount(DarkModeHarness, { props: { mode: 'auto' } });
136
+ await flushPromises();
137
+
138
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('false');
139
+
140
+ spy.mockRestore();
141
+ wrapper.unmount();
142
+ });
143
+
144
+ it('switches from "off" to "force" when mode changes', async () => {
145
+ const wrapper = mount(DarkModeInteractiveHarness, {
146
+ props: { initialMode: 'off' },
147
+ });
148
+ await flushPromises();
149
+
150
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('false');
151
+
152
+ await wrapper.find('[data-testid="set-force"]').trigger('click');
153
+ await flushPromises();
154
+
155
+ expect(wrapper.find('[data-testid="dark-mode"]').text()).toBe('true');
156
+ wrapper.unmount();
157
+ });
158
+ });
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Tests for useTableState composable.
3
+ *
4
+ * Uses thin wrapper components that expose composable state via the DOM
5
+ * and trigger state changes via button clicks.
6
+ */
7
+
8
+ import { flushPromises, mount } from '@vue/test-utils';
9
+ import { describe, expect, it } from 'vitest';
10
+ import { defineComponent, h } from 'vue';
11
+ import { type UseTableStateOptions, useTableState } from '../composables/useTableState';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Test harness: renders composable state to the DOM
15
+ // ---------------------------------------------------------------------------
16
+
17
+ const TableStateHarness = defineComponent({
18
+ props: {
19
+ initialState: {
20
+ type: Object,
21
+ default: undefined,
22
+ },
23
+ },
24
+ setup(props) {
25
+ const { sort, search, page, setSort, setSearch, setPage, resetState } = useTableState(
26
+ props.initialState as UseTableStateOptions | undefined,
27
+ );
28
+
29
+ return { sort, search, page, setSort, setSearch, setPage, resetState };
30
+ },
31
+ render() {
32
+ return h('div', [
33
+ h(
34
+ 'span',
35
+ { 'data-testid': 'sort' },
36
+ this.sort ? `${this.sort.column}:${this.sort.direction}` : 'null',
37
+ ),
38
+ h('span', { 'data-testid': 'search' }, this.search),
39
+ h('span', { 'data-testid': 'page' }, String(this.page)),
40
+ h(
41
+ 'button',
42
+ {
43
+ 'data-testid': 'set-sort-name-asc',
44
+ onClick: () => this.setSort({ column: 'name', direction: 'asc' }),
45
+ },
46
+ 'sort name asc',
47
+ ),
48
+ h(
49
+ 'button',
50
+ {
51
+ 'data-testid': 'set-sort-age-desc',
52
+ onClick: () => this.setSort({ column: 'age', direction: 'desc' }),
53
+ },
54
+ 'sort age desc',
55
+ ),
56
+ h(
57
+ 'button',
58
+ {
59
+ 'data-testid': 'clear-sort',
60
+ onClick: () => this.setSort(null),
61
+ },
62
+ 'clear sort',
63
+ ),
64
+ h(
65
+ 'button',
66
+ {
67
+ 'data-testid': 'set-search',
68
+ onClick: () => this.setSearch('filter text'),
69
+ },
70
+ 'set search',
71
+ ),
72
+ h(
73
+ 'button',
74
+ {
75
+ 'data-testid': 'set-page-5',
76
+ onClick: () => this.setPage(5),
77
+ },
78
+ 'page 5',
79
+ ),
80
+ h(
81
+ 'button',
82
+ {
83
+ 'data-testid': 'reset',
84
+ onClick: () => this.resetState(),
85
+ },
86
+ 'reset',
87
+ ),
88
+ ]);
89
+ },
90
+ });
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Helpers
94
+ // ---------------------------------------------------------------------------
95
+
96
+ async function mountHarness(initialState?: UseTableStateOptions) {
97
+ const wrapper = mount(TableStateHarness, {
98
+ props: initialState ? { initialState } : {},
99
+ });
100
+ await flushPromises();
101
+ return wrapper;
102
+ }
103
+
104
+ function getState(wrapper: ReturnType<typeof mount>) {
105
+ return {
106
+ sort: wrapper.find('[data-testid="sort"]').text(),
107
+ search: wrapper.find('[data-testid="search"]').text(),
108
+ page: wrapper.find('[data-testid="page"]').text(),
109
+ };
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Tests
114
+ // ---------------------------------------------------------------------------
115
+
116
+ describe('useTableState', () => {
117
+ // -------------------------------------------------------------------------
118
+ // Default initial state
119
+ // -------------------------------------------------------------------------
120
+
121
+ it('initializes with default values when no options provided', async () => {
122
+ const wrapper = await mountHarness();
123
+ const state = getState(wrapper);
124
+
125
+ expect(state.sort).toBe('null');
126
+ expect(state.search).toBe('');
127
+ expect(state.page).toBe('0');
128
+ wrapper.unmount();
129
+ });
130
+
131
+ // -------------------------------------------------------------------------
132
+ // Custom initial state
133
+ // -------------------------------------------------------------------------
134
+
135
+ it('initializes with provided sort state', async () => {
136
+ const wrapper = await mountHarness({
137
+ sort: { column: 'name', direction: 'asc' },
138
+ });
139
+ expect(getState(wrapper).sort).toBe('name:asc');
140
+ wrapper.unmount();
141
+ });
142
+
143
+ it('initializes with provided search string', async () => {
144
+ const wrapper = await mountHarness({ search: 'hello' });
145
+ expect(getState(wrapper).search).toBe('hello');
146
+ wrapper.unmount();
147
+ });
148
+
149
+ it('initializes with provided page number', async () => {
150
+ const wrapper = await mountHarness({ page: 3 });
151
+ expect(getState(wrapper).page).toBe('3');
152
+ wrapper.unmount();
153
+ });
154
+
155
+ // -------------------------------------------------------------------------
156
+ // State setters
157
+ // -------------------------------------------------------------------------
158
+
159
+ it('setSort updates the sort state', async () => {
160
+ const wrapper = await mountHarness();
161
+
162
+ await wrapper.find('[data-testid="set-sort-age-desc"]').trigger('click');
163
+ await flushPromises();
164
+
165
+ expect(getState(wrapper).sort).toBe('age:desc');
166
+ wrapper.unmount();
167
+ });
168
+
169
+ it('setSort can clear sort by passing null', async () => {
170
+ const wrapper = await mountHarness({
171
+ sort: { column: 'name', direction: 'asc' },
172
+ });
173
+ expect(getState(wrapper).sort).toBe('name:asc');
174
+
175
+ await wrapper.find('[data-testid="clear-sort"]').trigger('click');
176
+ await flushPromises();
177
+
178
+ expect(getState(wrapper).sort).toBe('null');
179
+ wrapper.unmount();
180
+ });
181
+
182
+ it('setSearch updates the search query', async () => {
183
+ const wrapper = await mountHarness();
184
+
185
+ await wrapper.find('[data-testid="set-search"]').trigger('click');
186
+ await flushPromises();
187
+
188
+ expect(getState(wrapper).search).toBe('filter text');
189
+ wrapper.unmount();
190
+ });
191
+
192
+ it('setPage updates the current page', async () => {
193
+ const wrapper = await mountHarness();
194
+
195
+ await wrapper.find('[data-testid="set-page-5"]').trigger('click');
196
+ await flushPromises();
197
+
198
+ expect(getState(wrapper).page).toBe('5');
199
+ wrapper.unmount();
200
+ });
201
+
202
+ // -------------------------------------------------------------------------
203
+ // resetState
204
+ // -------------------------------------------------------------------------
205
+
206
+ it('resetState restores default initial values', async () => {
207
+ const wrapper = await mountHarness();
208
+
209
+ // Change all values
210
+ await wrapper.find('[data-testid="set-sort-name-asc"]').trigger('click');
211
+ await wrapper.find('[data-testid="set-search"]').trigger('click');
212
+ await wrapper.find('[data-testid="set-page-5"]').trigger('click');
213
+ await flushPromises();
214
+
215
+ expect(getState(wrapper).sort).toBe('name:asc');
216
+ expect(getState(wrapper).search).toBe('filter text');
217
+ expect(getState(wrapper).page).toBe('5');
218
+
219
+ // Reset
220
+ await wrapper.find('[data-testid="reset"]').trigger('click');
221
+ await flushPromises();
222
+
223
+ expect(getState(wrapper).sort).toBe('null');
224
+ expect(getState(wrapper).search).toBe('');
225
+ expect(getState(wrapper).page).toBe('0');
226
+ wrapper.unmount();
227
+ });
228
+
229
+ it('resetState restores custom initial values', async () => {
230
+ const initialState = {
231
+ sort: { column: 'age', direction: 'desc' as const },
232
+ search: 'initial',
233
+ page: 1,
234
+ };
235
+ const wrapper = await mountHarness(initialState);
236
+
237
+ // Change all values
238
+ await wrapper.find('[data-testid="clear-sort"]').trigger('click');
239
+ await wrapper.find('[data-testid="set-search"]').trigger('click');
240
+ await wrapper.find('[data-testid="set-page-5"]').trigger('click');
241
+ await flushPromises();
242
+
243
+ expect(getState(wrapper).sort).toBe('null');
244
+ expect(getState(wrapper).search).toBe('filter text');
245
+ expect(getState(wrapper).page).toBe('5');
246
+
247
+ // Reset to initial
248
+ await wrapper.find('[data-testid="reset"]').trigger('click');
249
+ await flushPromises();
250
+
251
+ expect(getState(wrapper).sort).toBe('age:desc');
252
+ expect(getState(wrapper).search).toBe('initial');
253
+ expect(getState(wrapper).page).toBe('1');
254
+ wrapper.unmount();
255
+ });
256
+ });