brut-js 0.0.1

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,241 @@
1
+ import { withHTML, readRequestBodyIntoString, waitForSetTimeout } from "../src/testing/index.js"
2
+
3
+ describe("<brut-ajax-submit>", () => {
4
+ withHTML(`
5
+ <form action="http://example.net/foo" method="POST">
6
+ <input required type="text" name="some-text">
7
+ <input required type="number" name="some-number">
8
+ <brut-ajax-submit submitted-lifetime="10">
9
+ <button>Submit</button>
10
+ </brut-ajax-submit>
11
+ </form>
12
+ `).onFetch( "/foo", [
13
+ { then: { status: 200 }},
14
+ ]
15
+ ).test("submits the form, setting various attributes during the lifecycle", ({document,assert,fetchRequests}) => {
16
+ const element = document.querySelector("brut-ajax-submit")
17
+ const button = element.querySelector("button")
18
+ const text = document.querySelector("input[name=some-text]")
19
+ const number = document.querySelector("input[name=some-number]")
20
+
21
+ let okReceived = false
22
+ element.addEventListener("brut:submitok", () => {
23
+ okReceived = true
24
+ })
25
+
26
+ text.value = "Some Text"
27
+ number.value = "11"
28
+
29
+ button.click()
30
+ assert(element.getAttribute("requesting") != null)
31
+
32
+ const promises = fetchRequests.
33
+ filter( (fetchRequest) => fetchRequest.promiseReturned ).
34
+ map( (fetchRequest) => fetchRequest.promiseReturned )
35
+
36
+ return Promise.all(promises).then( () => {
37
+ assert(okReceived)
38
+ assert(element.getAttribute("requesting") == null)
39
+ assert(element.getAttribute("submitted") != null)
40
+ waitForSetTimeout(11).then( () => {
41
+ assert(element.getAttribute("submitted") == null)
42
+ return readRequestBodyIntoString(fetchRequests[0]).then( (string) => {
43
+ const params = new URLSearchParams(string)
44
+ assert.equal(params.get("some-text"),"Some Text")
45
+ assert.equal(params.get("some-number"),"11")
46
+ })
47
+ })
48
+
49
+ })
50
+ }).test("does not submit the form if it's invalid", ({document,assert,fetchRequests}) => {
51
+
52
+ const form = document.querySelector("form")
53
+ const element = form.querySelector("brut-ajax-submit")
54
+ const button = element.querySelector("button")
55
+ const text = form.querySelector("input[name=some-text]")
56
+ const number = form.querySelector("input[name=some-number]")
57
+
58
+ let okReceived = false
59
+ element.addEventListener("brut:submitok", () => {
60
+ okReceived = true
61
+ })
62
+
63
+ const isValid = form.reportValidity()
64
+ assert(!isValid)
65
+
66
+ button.click()
67
+
68
+ assert.equal(fetchRequests.length,0)
69
+ assert(!okReceived)
70
+ })
71
+ withHTML(`
72
+ <form action="http://example.net/foo" method="POST">
73
+ <input required type="text" name="some-text">
74
+ <input required type="number" name="some-number">
75
+ <brut-ajax-submit submitted-lifetime="10">
76
+ <button>Submit</button>
77
+ </brut-ajax-submit>
78
+ </form>
79
+ `).onFetch( "/foo", [
80
+ { then: { status: 500 }},
81
+ { then: { status: 200 }},
82
+ ]
83
+ ).test("submits the form after a retry", ({document,assert,fetchRequests}) => {
84
+ const element = document.querySelector("brut-ajax-submit")
85
+ const button = element.querySelector("button")
86
+ const text = document.querySelector("input[name=some-text]")
87
+ const number = document.querySelector("input[name=some-number]")
88
+
89
+ let okReceived = 0
90
+ element.addEventListener("brut:submitok", () => {
91
+ okReceived++
92
+ })
93
+
94
+ text.value = "Some Text"
95
+ number.value = "11"
96
+
97
+ button.click()
98
+ return waitForSetTimeout(5).then( () => {
99
+
100
+ const promises = fetchRequests.
101
+ filter( (fetchRequest) => fetchRequest.promiseReturned ).
102
+ map( (fetchRequest) => fetchRequest.promiseReturned )
103
+
104
+ return Promise.all(promises).then( () => {
105
+ assert.equal(okReceived,1)
106
+ return readRequestBodyIntoString(fetchRequests[1]).then( (string) => {
107
+ const params = new URLSearchParams(string)
108
+ assert.equal(params.get("some-text"),"Some Text")
109
+ assert.equal(params.get("some-number"),"11")
110
+ })
111
+ })
112
+ })
113
+ })
114
+ withHTML(`
115
+ <form action="http://example.net/foo" method="POST">
116
+ <input required type="text" name="some-text">
117
+ <input required type="number" name="some-number">
118
+ <brut-ajax-submit submitted-lifetime="10" max-retry-attempts=2>
119
+ <button>Submit</button>
120
+ </brut-ajax-submit>
121
+ </form>
122
+ `).onFetch( "/foo", [
123
+ { then: { status: 500 }},
124
+ { then: { status: 500 }},
125
+ { then: { status: 500 }},
126
+ ]
127
+ ).test("when too many failures, submits the form the old-fashioned way", ({document,assert,fetchRequests}) => {
128
+ const form = document.querySelector("form")
129
+ const element = form.querySelector("brut-ajax-submit")
130
+ const button = element.querySelector("button")
131
+ const text = form.querySelector("input[name=some-text]")
132
+ const number = form.querySelector("input[name=some-number]")
133
+
134
+ let okReceived = 0
135
+ element.addEventListener("brut:submitok", () => {
136
+ okReceived++
137
+ })
138
+
139
+ text.value = "Some Text"
140
+ number.value = "11"
141
+
142
+ let submitted = false
143
+ form.addEventListener("submit", (event) => {
144
+ event.preventDefault()
145
+ submitted = true
146
+ })
147
+
148
+ button.click()
149
+ return waitForSetTimeout(50).then( () => {
150
+
151
+ const promises = fetchRequests.
152
+ filter( (fetchRequest) => fetchRequest.promiseReturned ).
153
+ map( (fetchRequest) => fetchRequest.promiseReturned )
154
+
155
+ return Promise.all(promises).then( () => {
156
+ assert.equal(okReceived,0)
157
+ assert(submitted)
158
+ })
159
+ })
160
+ })
161
+ withHTML(`
162
+ <form action="http://example.net/foo" method="POST">
163
+
164
+ <input required type="text" name="some-text">
165
+ <brut-constraint-violation-messages server-side input-name="some-text">
166
+ </brut-constraint-violation-messages>
167
+
168
+ <input required type="number" name="some-number">
169
+ <brut-constraint-violation-messages server-side input-name="some-number">
170
+ </brut-constraint-violation-messages>
171
+
172
+ <brut-ajax-submit submitted-lifetime="10">
173
+ <button>Submit</button>
174
+ </brut-ajax-submit>
175
+ </form>
176
+ `).onFetch( "/foo", [
177
+ {
178
+ then: {
179
+ status: 422,
180
+ text: `
181
+ <brut-constraint-violation-message input-name="some-text">
182
+ A sever-side error
183
+ </brut-constraint-violation-message>
184
+ <brut-constraint-violation-message input-name="some-text">
185
+ Another sever-side error
186
+ </brut-constraint-violation-message>
187
+ <brut-constraint-violation-message input-name="some-other-text">
188
+ Irrelevant server-side error
189
+ </brut-constraint-violation-message>
190
+ <brut-constraint-violation-message>
191
+ Error that should be ignored
192
+ </brut-constraint-violation-message>
193
+ <div>element that should be ignored</div>
194
+ `
195
+ }
196
+ },
197
+ ]
198
+ ).test("when we get a 422, parses the result from the server", ({document,window,assert,fetchRequests}) => {
199
+ const form = document.querySelector("form")
200
+ const element = form.querySelector("brut-ajax-submit")
201
+ const button = element.querySelector("button")
202
+ const text = form.querySelector("input[name=some-text]")
203
+ const number = form.querySelector("input[name=some-number]")
204
+
205
+ let okReceived = 0
206
+ element.addEventListener("brut:submitok", () => {
207
+ okReceived++
208
+ })
209
+
210
+ text.value = "Some Text"
211
+ number.value = "11"
212
+
213
+ button.click()
214
+ return waitForSetTimeout(5).then( () => {
215
+
216
+ const promises = fetchRequests.
217
+ filter( (fetchRequest) => fetchRequest.promiseReturned ).
218
+ map( (fetchRequest) => fetchRequest.promiseReturned )
219
+
220
+ return Promise.all(promises).then( () => {
221
+ assert.equal(okReceived,0)
222
+ const textFieldErrors = form.querySelectorAll("brut-constraint-violation-messages[input-name='some-text'] brut-constraint-violation-message")
223
+ assert.equal(2,textFieldErrors.length)
224
+
225
+ let error = Array.from(textFieldErrors).find( (e) => e.textContent.trim() == "A sever-side error" )
226
+ assert(error)
227
+ error = Array.from(textFieldErrors).find( (e) => e.textContent.trim() == "Another sever-side error" )
228
+ assert(error)
229
+
230
+ const numberFieldErrors = form.querySelectorAll("brut-constraint-violation-messages[input-name=some-number] brut-constraint-violation-message")
231
+ assert.equal(0,numberFieldErrors.length)
232
+
233
+ assert(text.validity.customError)
234
+ assert(!number.validity.customError)
235
+
236
+ text.dispatchEvent(new window.Event("change"))
237
+ assert(!text.validity.customError)
238
+ })
239
+ })
240
+ })
241
+ })
@@ -0,0 +1,127 @@
1
+ import { withHTML } from "../src/testing/index.js"
2
+
3
+ describe("<brut-autosubmit>", () => {
4
+ withHTML(`
5
+ <form id="form-1">
6
+ <brut-autosubmit>
7
+ <select name="status">
8
+ <option value="draft">Draft</option>
9
+ <option value="ready">Ready</option>
10
+ <option value="published">Published</option>
11
+ </select>
12
+ <textarea></textarea>
13
+ <input type="text" id="input-1">
14
+ <input type="text" id="input-2" form="form-2">
15
+ </brut-autosubmit>
16
+ <button>Save</button>
17
+ </form>
18
+ <form id="form-2">
19
+ <button>Save</button>
20
+ </form>
21
+ `).test("<select> 'change' event submits the form", ({window,document,assert}) => {
22
+
23
+ const form = document.getElementById("form-1")
24
+ const select = form.querySelector("select")
25
+
26
+ let submitted = false
27
+
28
+ form.addEventListener("submit", (event) => {
29
+ event.preventDefault()
30
+ submitted = true
31
+ })
32
+ const event = new window.Event("change",{})
33
+ select.dispatchEvent(event)
34
+ assert(submitted)
35
+ }).test("<textarea> 'change' event submits the form", ({window,document,assert}) => {
36
+
37
+ const form = document.getElementById("form-1")
38
+ const textarea = form.querySelector("textarea")
39
+
40
+ let submitted = false
41
+
42
+ form.addEventListener("submit", (event) => {
43
+ event.preventDefault()
44
+ submitted = true
45
+ })
46
+ const event = new window.Event("change",{})
47
+ textarea.dispatchEvent(event)
48
+ assert(submitted)
49
+ }).test("<input> 'change' event submits the form", ({window,document,assert}) => {
50
+
51
+ const form = document.getElementById("form-1")
52
+ const input = document.getElementById("input-1")
53
+
54
+ let submitted = false
55
+
56
+ form.addEventListener("submit", (event) => {
57
+ event.preventDefault()
58
+ submitted = true
59
+ })
60
+ const event = new window.Event("change",{})
61
+ input.dispatchEvent(event)
62
+ assert(submitted)
63
+ }).test("<select> 'input' event does not submit the form", ({window,document,assert}) => {
64
+
65
+ const form = document.getElementById("form-1")
66
+ const select = form.querySelector("select")
67
+
68
+ let submitted = false
69
+
70
+ form.addEventListener("submit", (event) => {
71
+ event.preventDefault()
72
+ submitted = true
73
+ })
74
+ const event = new window.Event("input",{})
75
+ select.dispatchEvent(event)
76
+ assert(!submitted)
77
+ }).test("<textarea> 'input' event does not submit the form", ({window,document,assert}) => {
78
+
79
+ const form = document.getElementById("form-1")
80
+ const textarea = form.querySelector("textarea")
81
+
82
+ let submitted = false
83
+
84
+ form.addEventListener("submit", (event) => {
85
+ event.preventDefault()
86
+ submitted = true
87
+ })
88
+ const event = new window.Event("input",{})
89
+ textarea.dispatchEvent(event)
90
+ assert(!submitted)
91
+ }).test("<input> 'input' event does not submit the form", ({window,document,assert}) => {
92
+
93
+ const form = document.getElementById("form-1")
94
+ const input = document.getElementById("input-1")
95
+
96
+ let submitted = false
97
+
98
+ form.addEventListener("submit", (event) => {
99
+ event.preventDefault()
100
+ submitted = true
101
+ })
102
+ const event = new window.Event("input",{})
103
+ input.dispatchEvent(event)
104
+ assert(!submitted)
105
+ }).test("'change' event for element related to another form does nothing", ({window,document,assert}) => {
106
+
107
+ const form1 = document.getElementById("form-1")
108
+ const form2 = document.getElementById("form-1")
109
+ const input = document.getElementById("input-2")
110
+
111
+ let submitted1 = false
112
+ let submitted2 = false
113
+
114
+ form1.addEventListener("submit", (event) => {
115
+ event.preventDefault()
116
+ submitted1 = true
117
+ })
118
+ form2.addEventListener("submit", (event) => {
119
+ event.preventDefault()
120
+ submitted2 = true
121
+ })
122
+ const event = new window.Event("input",{})
123
+ input.dispatchEvent(event)
124
+ assert(!submitted1)
125
+ assert(!submitted2)
126
+ })
127
+ })
@@ -0,0 +1,193 @@
1
+ import { withHTML } from "../src/testing/index.js"
2
+
3
+ describe("<brut-confirm-submit>", () => {
4
+ describe("without an explicit dialog", () => {
5
+ withHTML(`
6
+ <form>
7
+ <input type="text" name="text">
8
+ <brut-confirm-submit message='You sure?'>
9
+ <button>Save</button>
10
+ <input type="submit" value="Submit">
11
+ </brut-confirm-submit>
12
+ </form>
13
+ `).test("uses window.confirm and cancels the click on Cancel", ({window,document,assert}) => {
14
+ const form = document.querySelector("form")
15
+ const button = document.querySelector("button")
16
+
17
+ let submitted = false
18
+ form.addEventListener("submit", (event) => {
19
+ event.preventDefault()
20
+ submitted = true
21
+ })
22
+
23
+ let shown = false
24
+ let messageShown = null
25
+
26
+ window.confirm = (message) => {
27
+ shown = true
28
+ messageShown = message
29
+ return false // "Cancel"
30
+ }
31
+
32
+ button.click()
33
+ assert(shown)
34
+ assert.equal(messageShown,"You sure?")
35
+ assert(!submitted)
36
+ }).test("uses window.confirm and allows the click on OK", ({window,document,assert}) => {
37
+ const form = document.querySelector("form")
38
+ const button = document.querySelector("button")
39
+
40
+ let submitted = false
41
+ form.addEventListener("submit", (event) => {
42
+ event.preventDefault()
43
+ submitted = true
44
+ })
45
+
46
+ let shown = false
47
+ let messageShown = null
48
+
49
+ window.confirm = (message) => {
50
+ shown = true
51
+ messageShown = message
52
+ return true // "OK"
53
+ }
54
+
55
+
56
+ button.click()
57
+ assert(shown)
58
+ assert.equal(messageShown,"You sure?")
59
+ assert(submitted)
60
+ }).test("uses window.confirm on submit button", ({window,document,assert}) => {
61
+ const form = document.querySelector("form")
62
+ const button = document.querySelector("input[type=submit]")
63
+
64
+ let submitted = false
65
+ form.addEventListener("submit", (event) => {
66
+ event.preventDefault()
67
+ submitted = true
68
+ })
69
+
70
+ let shown = false
71
+ let messageShown = null
72
+
73
+ window.confirm = (message) => {
74
+ shown = true
75
+ messageShown = message
76
+ return true // "OK"
77
+ }
78
+
79
+
80
+ button.click()
81
+ assert(shown)
82
+ assert.equal(messageShown,"You sure?")
83
+ assert(submitted)
84
+ })
85
+ })
86
+ describe("with an implicit dialog", () => {
87
+ withHTML(`
88
+ <form>
89
+ <input type="text" name="text">
90
+ <brut-confirm-submit message='You sure?' show-warnings>
91
+ <button>Save</button>
92
+ <input type="submit" value="Submit">
93
+ </brut-confirm-submit>
94
+ </form>
95
+ <brut-confirmation-dialog>
96
+ <dialog>
97
+ <h1></h1>
98
+ <button value="ok">OK</button>
99
+ <button value="cancel">Cancel</button>
100
+ </dialog>
101
+ </brut-confirmation-dialog>
102
+ `).test("uses the dialog and cancels the click on Cancel", ({window,document,assert}) => {
103
+ const form = document.querySelector("form")
104
+ const button = document.querySelector("button")
105
+ const dialog = document.querySelector("dialog")
106
+
107
+ let submitted = false
108
+ form.addEventListener("submit", (event) => {
109
+ event.preventDefault()
110
+ submitted = true
111
+ })
112
+
113
+ button.click()
114
+
115
+ assert(dialog.open)
116
+ const message = dialog.querySelector("h1").textContent
117
+ assert.equal(message,"You sure?")
118
+
119
+ const cancel = dialog.querySelector("button[value=cancel]")
120
+ cancel.click()
121
+
122
+ assert(!submitted)
123
+ }).test("uses the dialog and submits the click on OK", ({window,document,assert}) => {
124
+ const form = document.querySelector("form")
125
+ const button = document.querySelector("button")
126
+ const dialog = document.querySelector("dialog")
127
+
128
+ let submitted = false
129
+ form.addEventListener("submit", (event) => {
130
+ event.preventDefault()
131
+ submitted = true
132
+ })
133
+
134
+ button.click()
135
+
136
+ assert(dialog.open)
137
+ const message = dialog.querySelector("h1").textContent
138
+ assert.equal("You sure?",message)
139
+
140
+ const ok = dialog.querySelector("button[value=ok]")
141
+ assert.equal(ok.textContent,button.textContent)
142
+ ok.click()
143
+ assert(submitted)
144
+ })
145
+ })
146
+ describe("with multiple dialogs", () => {
147
+ withHTML(`
148
+ <form>
149
+ <input type="text" name="text">
150
+ <brut-confirm-submit message='You sure?' dialog="dialog-2" show-warnings>
151
+ <button>Save</button>
152
+ <input type="submit" value="Submit">
153
+ </brut-confirm-submit>
154
+ </form>
155
+ <brut-confirmation-dialog id="dialog-1">
156
+ <dialog>
157
+ <h1></h1>
158
+ <button value="ok">OK</button>
159
+ <button value="cancel">Cancel</button>
160
+ </dialog>
161
+ </brut-confirmation-dialog>
162
+ <brut-confirmation-dialog id="dialog-2">
163
+ <dialog>
164
+ <h1></h1>
165
+ <button value="ok">OK</button>
166
+ <button value="cancel">Cancel</button>
167
+ </dialog>
168
+ </brut-confirmation-dialog>
169
+ `).test("click event uses the identified dialog", ({window,document,assert}) => {
170
+ const form = document.querySelector("form")
171
+ const button = form.querySelector("button")
172
+ const dialog = document.getElementById("dialog-2").querySelector("dialog")
173
+
174
+ let submitted = false
175
+ form.addEventListener("submit", (event) => {
176
+ event.preventDefault()
177
+ submitted = true
178
+ })
179
+
180
+ button.click()
181
+
182
+ assert(dialog.open)
183
+
184
+ const message = dialog.querySelector("h1").textContent
185
+ assert.equal("You sure?",message)
186
+
187
+ const ok = dialog.querySelector("button[value=ok]")
188
+ assert.equal(ok.textContent,button.textContent)
189
+ ok.click()
190
+ assert(submitted)
191
+ })
192
+ })
193
+ })
@@ -0,0 +1,33 @@
1
+ import { withHTML } from "../src/testing/index.js"
2
+
3
+ describe("<brut-constraint-violation-message>", () => {
4
+ withHTML(`
5
+ <brut-i18n-translation key="problem" value="%{field} has a problem"></brut-i18n-translation>
6
+
7
+ <brut-constraint-violation-message input-name="some-field" key="problem"></brut-constraint-violation-message>
8
+ `).test("Inserts using 'this field' as the placeholder", ({document,assert}) => {
9
+ const element = document.querySelector("brut-constraint-violation-message")
10
+ assert.equal(element.textContent,"This field has a problem")
11
+ })
12
+
13
+ withHTML(`
14
+ <brut-i18n-translation key="problem" value="%{field} has a problem"></brut-i18n-translation>
15
+ <brut-i18n-translation key="general.cv.this_field" value="THAT FIELD"></brut-i18n-translation>
16
+
17
+ <brut-constraint-violation-message input-name="some-field" key="problem"></brut-constraint-violation-message>
18
+ `).test("Inserts using the this_field key as the placeholder", ({document,assert}) => {
19
+ const element = document.querySelector("brut-constraint-violation-message")
20
+ assert.equal(element.textContent,"THAT FIELD has a problem")
21
+ })
22
+
23
+ withHTML(`
24
+ <brut-i18n-translation key="problem" value="%{field} has a problem"></brut-i18n-translation>
25
+ <brut-i18n-translation key="general.cv.this_field" value="THAT FIELD"></brut-i18n-translation>
26
+ <brut-i18n-translation key="general.cv.fe.fieldNames.some-field" value="Some Field"></brut-i18n-translation>
27
+
28
+ <brut-constraint-violation-message input-name="some-field" key="problem"></brut-constraint-violation-message>
29
+ `).test("Inserts using the this_field key as the placeholder", ({document,assert}) => {
30
+ const element = document.querySelector("brut-constraint-violation-message")
31
+ assert.equal(element.textContent,"Some Field has a problem")
32
+ })
33
+ })
@@ -0,0 +1,27 @@
1
+ import { withHTML } from "../src/testing/index.js"
2
+
3
+ describe("<brut-constraint-violation-messages>", () => {
4
+ withHTML(`
5
+ <brut-i18n-translation key="general.cv.fe.patternMismatch" value="%{field} does not match the pattern"></brut-i18n-translation>
6
+ <brut-i18n-translation key="general.cv.fe.rangeOverflow" value="%{field} is above the range"></brut-i18n-translation>
7
+
8
+ <brut-constraint-violation-messages input-name="some-field"></brut-constraint-violation-messages>
9
+ `).test("Inserts constraint violation messages based on validity state", ({document,assert}) => {
10
+ const element = document.querySelector("brut-constraint-violation-messages")
11
+
12
+ const validityState = {
13
+ patternMismatch: true,
14
+ rangeOverflow: true,
15
+ }
16
+ const inputName = "some-field"
17
+
18
+ element.createMessages({validityState,inputName})
19
+
20
+ assert.match(element.textContent,new RegExp("This field does not match the pattern","m"))
21
+ assert.match(element.textContent,new RegExp("This field is above the range","m"))
22
+
23
+ element.clearMessages()
24
+ assert.equal(element.textContent,"")
25
+ })
26
+
27
+ })