enigmatic 0.24.0 → 0.26.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,217 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ // Load e2.js into jsdom
5
+ const e2Code = fs.readFileSync(path.join(__dirname, '../e2.js'), 'utf8')
6
+
7
+ describe('e2.js', () => {
8
+ beforeEach(() => {
9
+ // Reset DOM
10
+ global.document.body.innerHTML = ''
11
+ global.document.head.innerHTML = ''
12
+
13
+ // Clear window.custom
14
+ global.window.custom = {}
15
+
16
+ // Execute e2.js code
17
+ eval(e2Code)
18
+ })
19
+
20
+ describe('fetchJson', () => {
21
+ test('fetches JSON with GET method', async () => {
22
+ const mockData = { id: 1, name: 'Test' }
23
+ global.fetch = jest.fn(() =>
24
+ Promise.resolve({
25
+ ok: true,
26
+ status: 200,
27
+ statusText: 'OK',
28
+ headers: new Headers({ 'content-type': 'application/json' }),
29
+ json: () => Promise.resolve(mockData)
30
+ })
31
+ )
32
+
33
+ const result = await window.fetchJson('GET', 'http://test.com/api')
34
+
35
+ expect(global.fetch).toHaveBeenCalledWith(
36
+ 'http://test.com/api',
37
+ expect.objectContaining({
38
+ method: 'GET',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ credentials: 'include'
41
+ })
42
+ )
43
+ expect(result.data).toEqual(mockData)
44
+ expect(result.status).toBe(200)
45
+ expect(result.statusText).toBe('OK')
46
+ expect(result.headers).toBeDefined()
47
+ })
48
+
49
+ test('fetches JSON with POST method and body', async () => {
50
+ const mockData = { success: true }
51
+ const requestBody = { name: 'Test' }
52
+
53
+ global.fetch = jest.fn(() =>
54
+ Promise.resolve({
55
+ ok: true,
56
+ status: 201,
57
+ statusText: 'Created',
58
+ headers: new Headers(),
59
+ json: () => Promise.resolve(mockData)
60
+ })
61
+ )
62
+
63
+ const result = await window.fetchJson('POST', 'http://test.com/api', {
64
+ body: JSON.stringify(requestBody)
65
+ })
66
+
67
+ expect(global.fetch).toHaveBeenCalledWith(
68
+ 'http://test.com/api',
69
+ expect.objectContaining({
70
+ method: 'POST',
71
+ headers: { 'Content-Type': 'application/json' },
72
+ credentials: 'include',
73
+ body: JSON.stringify(requestBody)
74
+ })
75
+ )
76
+ expect(result.data).toEqual(mockData)
77
+ expect(result.status).toBe(201)
78
+ expect(result.statusText).toBe('Created')
79
+ })
80
+
81
+ test('fetches JSON with PUT method', async () => {
82
+ const mockData = { updated: true }
83
+
84
+ global.fetch = jest.fn(() =>
85
+ Promise.resolve({
86
+ ok: true,
87
+ status: 200,
88
+ statusText: 'OK',
89
+ headers: new Headers(),
90
+ json: () => Promise.resolve(mockData)
91
+ })
92
+ )
93
+
94
+ const result = await window.fetchJson('PUT', 'http://test.com/api/1', {
95
+ body: JSON.stringify({ name: 'Updated' })
96
+ })
97
+
98
+ expect(global.fetch).toHaveBeenCalledWith(
99
+ 'http://test.com/api/1',
100
+ expect.objectContaining({
101
+ method: 'PUT',
102
+ headers: { 'Content-Type': 'application/json' },
103
+ credentials: 'include'
104
+ })
105
+ )
106
+ expect(result.data).toEqual(mockData)
107
+ })
108
+
109
+ test('fetches JSON with DELETE method', async () => {
110
+ const mockData = { deleted: true }
111
+
112
+ global.fetch = jest.fn(() =>
113
+ Promise.resolve({
114
+ ok: true,
115
+ status: 200,
116
+ statusText: 'OK',
117
+ headers: new Headers(),
118
+ json: () => Promise.resolve(mockData)
119
+ })
120
+ )
121
+
122
+ const result = await window.fetchJson('DELETE', 'http://test.com/api/1')
123
+
124
+ expect(global.fetch).toHaveBeenCalledWith(
125
+ 'http://test.com/api/1',
126
+ expect.objectContaining({
127
+ method: 'DELETE',
128
+ headers: { 'Content-Type': 'application/json' },
129
+ credentials: 'include'
130
+ })
131
+ )
132
+ expect(result.data).toEqual(mockData)
133
+ })
134
+
135
+ test('overwrites custom headers with Content-Type', async () => {
136
+ global.fetch = jest.fn(() =>
137
+ Promise.resolve({
138
+ ok: true,
139
+ status: 200,
140
+ statusText: 'OK',
141
+ headers: new Headers(),
142
+ json: () => Promise.resolve({})
143
+ })
144
+ )
145
+
146
+ await window.fetchJson('GET', 'http://test.com/api', {
147
+ headers: { 'Authorization': 'Bearer token123' }
148
+ })
149
+
150
+ expect(global.fetch).toHaveBeenCalledWith(
151
+ 'http://test.com/api',
152
+ expect.objectContaining({
153
+ headers: {
154
+ 'Content-Type': 'application/json'
155
+ }
156
+ })
157
+ )
158
+ // Custom headers are overwritten by Content-Type
159
+ expect(global.fetch).not.toHaveBeenCalledWith(
160
+ expect.anything(),
161
+ expect.objectContaining({
162
+ headers: expect.objectContaining({
163
+ 'Authorization': 'Bearer token123'
164
+ })
165
+ })
166
+ )
167
+ })
168
+
169
+ test('always includes credentials', async () => {
170
+ global.fetch = jest.fn(() =>
171
+ Promise.resolve({
172
+ ok: true,
173
+ status: 200,
174
+ statusText: 'OK',
175
+ headers: new Headers(),
176
+ json: () => Promise.resolve({})
177
+ })
178
+ )
179
+
180
+ await window.fetchJson('GET', 'http://test.com/api')
181
+
182
+ expect(global.fetch).toHaveBeenCalledWith(
183
+ 'http://test.com/api',
184
+ expect.objectContaining({
185
+ credentials: 'include'
186
+ })
187
+ )
188
+ })
189
+
190
+ test('handles error responses', async () => {
191
+ global.fetch = jest.fn(() =>
192
+ Promise.resolve({
193
+ ok: false,
194
+ status: 404,
195
+ statusText: 'Not Found',
196
+ headers: new Headers(),
197
+ json: () => Promise.resolve({ error: 'Not found' })
198
+ })
199
+ )
200
+
201
+ const result = await window.fetchJson('GET', 'http://test.com/api')
202
+
203
+ expect(result.status).toBe(404)
204
+ expect(result.statusText).toBe('Not Found')
205
+ expect(result.data).toEqual({ error: 'Not found' })
206
+ })
207
+
208
+ test('handles network errors', async () => {
209
+ global.fetch = jest.fn(() =>
210
+ Promise.reject(new Error('Network error'))
211
+ )
212
+
213
+ await expect(window.fetchJson('GET', 'http://test.com/api')).rejects.toThrow('Network error')
214
+ })
215
+ })
216
+ })
217
+
package/e2.js CHANGED
@@ -1,6 +1,16 @@
1
1
  window.$ = document.querySelector.bind(document)
2
2
  window.$$ = document.querySelectorAll.bind(document)
3
3
 
4
+ window.fetchJson = async (method, url, opts) => {
5
+ const res = await fetch(url, { method, ...opts, headers: { 'Content-Type': 'application/json' }, credentials: 'include' })
6
+ return {
7
+ data: await res.json(),
8
+ status: res.status,
9
+ statusText: res.statusText,
10
+ headers: res.headers
11
+ }
12
+ }
13
+
4
14
  window.custom = {
5
15
  "hello-world": (data) => `Hello ${data}`,
6
16
  "hello-world-2": {
@@ -12,18 +22,17 @@ window.custom = {
12
22
  }
13
23
 
14
24
  window.state = new Proxy({}, {
15
- set(obj, prop, value) {
16
- obj[prop] = value
17
- console.log('set', prop, value);
18
- $$(`hello-world, hello-world-2`).forEach(el => {
19
- console.log('setting', el.tagName);
20
- const f = window.custom[el.tagName.toLowerCase()];
21
- if(typeof f === 'function') {
22
- el.innerHTML = f(value);
23
- } else {
24
- el.innerHTML = f.render(value);
25
- }
26
- });
27
- return true
28
- }
25
+ set(obj, prop, value) {
26
+ obj[prop] = value
27
+ $$(`[data="${prop}"]`).forEach(el => {
28
+ console.log('setting', el.tagName);
29
+ const f = window.custom[el.tagName.toLowerCase()];
30
+ if(typeof f === 'function') {
31
+ el.innerHTML = f(value);
32
+ } else {
33
+ el.innerHTML = f.render(value);
34
+ }
35
+ });
36
+ return true
37
+ }
29
38
  })
package/index.html CHANGED
@@ -1,5 +1,6 @@
1
1
  <script src="components.js"></script>
2
2
  <script src="enigmatic.js"></script>
3
+ <script src="e2.js"></script>
3
4
  <link rel="stylesheet" href="enigmatic.css">
4
5
 
5
6
  <body style="--cols:1fr; --rows:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr">
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "enigmatic",
3
3
  "main": "enigmatic.js",
4
- "version": "0.24.0",
4
+ "version": "0.26.0",
5
5
  "scripts": {
6
6
  "test": "jest",
7
7
  "test:watch": "jest --watch"