create-gardener 2.0.6 → 2.0.7

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,364 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { describe, test, expect, beforeEach, jest } from '@jest/globals';
6
+ import {
7
+ fetchElement,
8
+ appendElement,
9
+ createElement,
10
+ insertText,
11
+ replaceElement,
12
+ gardener
13
+ } from './gardener.js';
14
+
15
+ describe('fetchElement', () => {
16
+ beforeEach(() => {
17
+ document.body.innerHTML = '';
18
+ });
19
+
20
+ test('should fetch element by query selector', () => {
21
+ document.body.innerHTML = '<div class="test-class"></div>';
22
+ const element = fetchElement('.test-class');
23
+ expect(element).toBeTruthy();
24
+ expect(element.className).toBe('test-class');
25
+ });
26
+
27
+ test('should return null for non-existent element', () => {
28
+ const element = fetchElement('.non-existent');
29
+ expect(element).toBeNull();
30
+ });
31
+
32
+ test('should fetch element by id', () => {
33
+ document.body.innerHTML = '<div id="test-id"></div>';
34
+ const element = fetchElement('#test-id');
35
+ expect(element).toBeTruthy();
36
+ expect(element.id).toBe('test-id');
37
+ });
38
+ });
39
+
40
+ describe('appendElement', () => {
41
+ beforeEach(() => {
42
+ document.body.innerHTML = '';
43
+ });
44
+
45
+ test('should append child to parent element', () => {
46
+ const parent = document.createElement('div');
47
+ const child = document.createElement('span');
48
+ document.body.appendChild(parent);
49
+
50
+ appendElement(parent, child);
51
+ expect(parent.children.length).toBe(1);
52
+ expect(parent.firstChild).toBe(child);
53
+ });
54
+
55
+ test('should append child using parent selector string', () => {
56
+ document.body.innerHTML = '<div class="parent"></div>';
57
+ const child = document.createElement('span');
58
+
59
+ appendElement('.parent', child);
60
+ const parent = document.querySelector('.parent');
61
+ expect(parent.children.length).toBe(1);
62
+ expect(parent.firstChild).toBe(child);
63
+ });
64
+
65
+ test('should append multiple children', () => {
66
+ const parent = document.createElement('div');
67
+ const child1 = document.createElement('span');
68
+ const child2 = document.createElement('p');
69
+
70
+ appendElement(parent, child1);
71
+ appendElement(parent, child2);
72
+
73
+ expect(parent.children.length).toBe(2);
74
+ expect(parent.children[0]).toBe(child1);
75
+ expect(parent.children[1]).toBe(child2);
76
+ });
77
+ });
78
+
79
+ describe('createElement', () => {
80
+ test('should create element with given type', () => {
81
+ const element = createElement('div');
82
+ expect(element.tagName).toBe('DIV');
83
+ });
84
+
85
+ test('should create element with single class', () => {
86
+ const element = createElement('div', ['test-class']);
87
+ expect(element.classList.contains('test-class')).toBe(true);
88
+ });
89
+
90
+ test('should create element with multiple classes', () => {
91
+ const element = createElement('span', ['class1', 'class2', 'class3']);
92
+ expect(element.classList.contains('class1')).toBe(true);
93
+ expect(element.classList.contains('class2')).toBe(true);
94
+ expect(element.classList.contains('class3')).toBe(true);
95
+ });
96
+
97
+ test('should create element without class if not provided', () => {
98
+ const element = createElement('p');
99
+ expect(element.className).toBe('');
100
+ });
101
+ });
102
+
103
+ describe('insertText', () => {
104
+ test('should insert text into element', () => {
105
+ const element = document.createElement('div');
106
+ insertText(element, 'Hello World');
107
+ expect(element.innerText).toBe('Hello World');
108
+ });
109
+
110
+ test('should replace existing text', () => {
111
+ const element = document.createElement('div');
112
+ element.innerText = 'Old Text';
113
+ insertText(element, 'New Text');
114
+ expect(element.innerText).toBe('New Text');
115
+ });
116
+
117
+ test('should handle empty string', () => {
118
+ const element = document.createElement('div');
119
+ insertText(element, '');
120
+ expect(element.innerText).toBe('');
121
+ });
122
+ });
123
+
124
+ describe('replaceElement', () => {
125
+ beforeEach(() => {
126
+ document.body.innerHTML = '';
127
+ });
128
+
129
+ test('should replace element with another element', () => {
130
+ const original = document.createElement('div');
131
+ original.id = 'original';
132
+ const replacement = document.createElement('span');
133
+ replacement.id = 'replacement';
134
+ document.body.appendChild(original);
135
+
136
+ replaceElement(original, replacement);
137
+ expect(document.getElementById('original')).toBeNull();
138
+ expect(document.getElementById('replacement')).toBeTruthy();
139
+ });
140
+
141
+ test('should replace element using selector string', () => {
142
+ document.body.innerHTML = '<div class="original"></div>';
143
+ const replacement = document.createElement('span');
144
+ replacement.className = 'replacement';
145
+
146
+ replaceElement('.original', replacement);
147
+ expect(document.querySelector('.original')).toBeNull();
148
+ expect(document.querySelector('.replacement')).toBeTruthy();
149
+ });
150
+ });
151
+
152
+ describe('gardener', () => {
153
+ beforeEach(() => {
154
+ document.body.innerHTML = '';
155
+ });
156
+
157
+ test('should return DOM element if nodeType is 1', () => {
158
+ const element = document.createElement('div');
159
+ const result = gardener(element);
160
+ expect(result).toBe(element);
161
+ });
162
+
163
+ test('should create simple element', () => {
164
+ const dom = { t: 'div' };
165
+ const element = gardener(dom);
166
+ expect(element.tagName).toBe('DIV');
167
+ });
168
+
169
+ test('should create element with classes', () => {
170
+ const dom = { t: 'div', cn: ['class1', 'class2'] };
171
+ const element = gardener(dom);
172
+ expect(element.classList.contains('class1')).toBe(true);
173
+ expect(element.classList.contains('class2')).toBe(true);
174
+ });
175
+
176
+ test('should create element with text content', () => {
177
+ const dom = { t: 'p', txt: 'Hello World' };
178
+ const element = gardener(dom);
179
+ expect(element.innerText).toBe('Hello World');
180
+ });
181
+
182
+ test('should create element with attributes', () => {
183
+ const dom = {
184
+ t: 'input',
185
+ attr: { type: 'text', placeholder: 'Enter text', id: 'test-input' }
186
+ };
187
+ const element = gardener(dom);
188
+ expect(element.getAttribute('type')).toBe('text');
189
+ expect(element.getAttribute('placeholder')).toBe('Enter text');
190
+ expect(element.id).toBe('test-input');
191
+ });
192
+
193
+ test('should create element with data attributes', () => {
194
+ const dom = {
195
+ t: 'div',
196
+ attr: { 'data-test': 'value', 'data-id': '123' }
197
+ };
198
+ const element = gardener(dom);
199
+ expect(element.getAttribute('data-test')).toBe('value');
200
+ expect(element.getAttribute('data-id')).toBe('123');
201
+ });
202
+
203
+ test('should create element with aria attributes', () => {
204
+ const dom = {
205
+ t: 'button',
206
+ attr: { 'aria-label': 'Close', 'aria-expanded': 'false' }
207
+ };
208
+ const element = gardener(dom);
209
+ expect(element.getAttribute('aria-label')).toBe('Close');
210
+ expect(element.getAttribute('aria-expanded')).toBe('false');
211
+ });
212
+
213
+ test('should create element with event listeners', () => {
214
+ const mockHandler = jest.fn();
215
+ const dom = {
216
+ t: 'button',
217
+ events: { click: mockHandler }
218
+ };
219
+ const element = gardener(dom);
220
+ element.click();
221
+ expect(mockHandler).toHaveBeenCalledTimes(1);
222
+ });
223
+
224
+ test('should create element with multiple event listeners', () => {
225
+ const clickHandler = jest.fn();
226
+ const mouseoverHandler = jest.fn();
227
+ const dom = {
228
+ t: 'div',
229
+ events: { click: clickHandler, mouseover: mouseoverHandler }
230
+ };
231
+ const element = gardener(dom);
232
+
233
+ element.click();
234
+ element.dispatchEvent(new Event('mouseover'));
235
+
236
+ expect(clickHandler).toHaveBeenCalledTimes(1);
237
+ expect(mouseoverHandler).toHaveBeenCalledTimes(1);
238
+ });
239
+
240
+ test('should create nested elements', () => {
241
+ const dom = {
242
+ t: 'div',
243
+ children: [
244
+ { t: 'span', txt: 'Child 1' },
245
+ { t: 'p', txt: 'Child 2' }
246
+ ]
247
+ };
248
+ const element = gardener(dom);
249
+ expect(element.children.length).toBe(2);
250
+ expect(element.children[0].tagName).toBe('SPAN');
251
+ expect(element.children[0].innerText).toBe('Child 1');
252
+ expect(element.children[1].tagName).toBe('P');
253
+ expect(element.children[1].innerText).toBe('Child 2');
254
+ });
255
+
256
+ test('should create deeply nested elements', () => {
257
+ const dom = {
258
+ t: 'div',
259
+ children: [
260
+ {
261
+ t: 'section',
262
+ children: [
263
+ { t: 'h1', txt: 'Title' },
264
+ { t: 'p', txt: 'Content' }
265
+ ]
266
+ }
267
+ ]
268
+ };
269
+ const element = gardener(dom);
270
+ expect(element.children.length).toBe(1);
271
+ expect(element.children[0].tagName).toBe('SECTION');
272
+ expect(element.children[0].children.length).toBe(2);
273
+ });
274
+
275
+ test('should create SVG element', () => {
276
+ const dom = { t: 'svg' };
277
+ const element = gardener(dom);
278
+ expect(element.tagName).toBe('svg');
279
+ expect(element.namespaceURI).toBe('http://www.w3.org/2000/svg');
280
+ });
281
+
282
+ test('should create SVG path element', () => {
283
+ const dom = {
284
+ t: 'path',
285
+ attr: { d: 'M10 10 H 90 V 90 H 10 Z' }
286
+ };
287
+ const element = gardener(dom);
288
+ expect(element.tagName).toBe('path');
289
+ expect(element.namespaceURI).toBe('http://www.w3.org/2000/svg');
290
+ expect(element.getAttribute('d')).toBe('M10 10 H 90 V 90 H 10 Z');
291
+ });
292
+
293
+ test('should create SVG with classes', () => {
294
+ const dom = {
295
+ t: 'circle',
296
+ cn: ['svg-class'],
297
+ attr: { cx: '50', cy: '50', r: '40' }
298
+ };
299
+ const element = gardener(dom);
300
+ expect(element.classList.contains('svg-class')).toBe(true);
301
+ expect(element.getAttribute('cx')).toBe('50');
302
+ });
303
+
304
+ test('should create complex SVG structure', () => {
305
+ const dom = {
306
+ t: 'svg',
307
+ attr: { width: '100', height: '100' },
308
+ children: [
309
+ { t: 'circle', attr: { cx: '50', cy: '50', r: '40' } },
310
+ { t: 'rect', attr: { x: '10', y: '10', width: '30', height: '30' } }
311
+ ]
312
+ };
313
+ const element = gardener(dom);
314
+ expect(element.children.length).toBe(2);
315
+ expect(element.children[0].tagName).toBe('circle');
316
+ expect(element.children[1].tagName).toBe('rect');
317
+ });
318
+
319
+ test('should handle property attributes like value', () => {
320
+ const dom = {
321
+ t: 'input',
322
+ attr: { value: 'test-value', selectedIndex: '2' }
323
+ };
324
+ const element = gardener(dom);
325
+ expect(element.getAttribute('value')).toBe('test-value');
326
+ });
327
+
328
+ test('should handle empty string attribute as boolean', () => {
329
+ const dom = {
330
+ t: 'button',
331
+ attr: { disabled: '' }
332
+ };
333
+ const element = gardener(dom);
334
+ expect(element.disabled).toBe(true);
335
+ });
336
+
337
+ test('should create element with all features combined', () => {
338
+ const clickHandler = jest.fn();
339
+ const dom = {
340
+ t: 'div',
341
+ cn: ['container', 'main'],
342
+ txt: 'Container Text',
343
+ attr: { id: 'main-container', 'data-test': 'value' },
344
+ events: { click: clickHandler },
345
+ children: [
346
+ { t: 'h1', txt: 'Title', cn: ['title'] },
347
+ { t: 'p', txt: 'Paragraph' }
348
+ ]
349
+ };
350
+
351
+ const element = gardener(dom);
352
+
353
+ expect(element.tagName).toBe('DIV');
354
+ expect(element.classList.contains('container')).toBe(true);
355
+ expect(element.classList.contains('main')).toBe(true);
356
+ expect(element.innerText).toContain('Container Text');
357
+ expect(element.id).toBe('main-container');
358
+ expect(element.getAttribute('data-test')).toBe('value');
359
+ expect(element.children.length).toBe(2);
360
+
361
+ element.click();
362
+ expect(clickHandler).toHaveBeenCalled();
363
+ });
364
+ });
@@ -310,7 +310,6 @@ async function addComponent(txt, path) {
310
310
  if (!res.ok) console.error('wrong');
311
311
 
312
312
  const data = await res.json()
313
- console.log(data);
314
313
 
315
314
  }
316
315
  catch (err) {
@@ -328,7 +327,6 @@ function opnPagedialog(btn = true) {
328
327
  e.preventDefault()
329
328
  const data = new FormData(e.target);
330
329
  const input = Object.fromEntries(data.entries());
331
- console.log(input)
332
330
 
333
331
  const response = await fetch('/addpage', {
334
332
  method: 'POST',
@@ -337,7 +335,6 @@ function opnPagedialog(btn = true) {
337
335
  },
338
336
  body: JSON.stringify(input)
339
337
  }).then(res => res.json())
340
- console.log(response)
341
338
  opnPagedialog(false)
342
339
  window.location.href = `${input.page}`
343
340
  }
@@ -353,12 +350,10 @@ function opnPagedialog(btn = true) {
353
350
 
354
351
  })
355
352
 
356
- console.log('test')
357
353
  appendElement(body, dialog);
358
354
  fetchElement('.pathinput').focus();
359
355
  }
360
356
  else {
361
- console.log('removed')
362
357
  fetchElement('.addpageform').remove();
363
358
  }
364
359
  }
@@ -416,7 +411,6 @@ export function parser(element, isParent = true) {
416
411
  element = fetchElement(element);
417
412
  }
418
413
 
419
- console.log(element)
420
414
  const obj = {
421
415
  t: element.tagName.toLowerCase(),
422
416
  };
@@ -489,6 +483,9 @@ export class State {
489
483
  cb(this.value);
490
484
  this.cb.push(cb);
491
485
  }
486
+ unregisterCb(cb) {
487
+ this.cb = this.cb.filter(c => c !== cb);
488
+ }
492
489
  setTo(val) {
493
490
  this.value = val;
494
491
  this.cb.forEach(cb => { cb(val) });
File without changes