155
// Regression test for https://github.com/facebook/react/issues/6119
156
expect(container.firstChild.hasAttribute('value')).toBe(false);
157
});
158
+
159
+ // @gate enableCustomElementPropertySupport
160
+ it('custom element custom events lowercase', () => {
161
+ const oncustomevent = jest.fn();
162
+ function Test() {
163
+ return <my-custom-element oncustomevent={oncustomevent} />;
164
+ }
165
+ const container = document.createElement('div');
166
+ ReactDOM.render(<Test />, container);
167
+ container
168
+ .querySelector('my-custom-element')
169
+ .dispatchEvent(new Event('customevent'));
170
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
171
+ });
172
+
173
+ // @gate enableCustomElementPropertySupport
174
+ it('custom element custom events uppercase', () => {
175
+ const oncustomevent = jest.fn();
176
+ function Test() {
177
+ return <my-custom-element onCustomevent={oncustomevent} />;
178
+ }
179
+ const container = document.createElement('div');
180
+ ReactDOM.render(<Test />, container);
181
+ container
182
+ .querySelector('my-custom-element')
183
+ .dispatchEvent(new Event('Customevent'));
184
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
185
+ });
186
+
187
+ // @gate enableCustomElementPropertySupport
188
+ it('custom element custom event with dash in name', () => {
189
+ const oncustomevent = jest.fn();
190
+ function Test() {
191
+ return <my-custom-element oncustom-event={oncustomevent} />;
192
+ }
193
+ const container = document.createElement('div');
194
+ ReactDOM.render(<Test />, container);
195
+ container
196
+ .querySelector('my-custom-element')
197
+ .dispatchEvent(new Event('custom-event'));
198
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
199
+ });
200
+
201
+ // @gate enableCustomElementPropertySupport
202
+ it('custom element remove event handler', () => {
203
+ const oncustomevent = jest.fn();
204
+ function Test(props) {
205
+ return <my-custom-element oncustomevent={props.handler} />;
206
+ }
207
+
208
+ const container = document.createElement('div');
209
+ ReactDOM.render(<Test handler={oncustomevent} />, container);
210
+ const customElement = container.querySelector('my-custom-element');
211
+ customElement.dispatchEvent(new Event('customevent'));
212
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
213
+
214
+ ReactDOM.render(<Test handler={false} />, container);
215
+ // Make sure that the second render didn't create a new element. We want
216
+ // to make sure removeEventListener actually gets called on the same element.
217
+ expect(customElement).toBe(customElement);
218
+ customElement.dispatchEvent(new Event('customevent'));
219
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
220
+
221
+ ReactDOM.render(<Test handler={oncustomevent} />, container);
222
+ customElement.dispatchEvent(new Event('customevent'));
223
+ expect(oncustomevent).toHaveBeenCalledTimes(2);
224
+
225
+ const oncustomevent2 = jest.fn();
226
+ ReactDOM.render(<Test handler={oncustomevent2} />, container);
227
+ customElement.dispatchEvent(new Event('customevent'));
228
+ expect(oncustomevent).toHaveBeenCalledTimes(2);
229
+ expect(oncustomevent2).toHaveBeenCalledTimes(1);
230
+ });
231
+
232
+ it('custom elements shouldnt have non-functions for on* attributes treated as event listeners', () => {
233
+ const container = document.createElement('div');
234
+ ReactDOM.render(
235
+ <my-custom-element
236
+ onstring={'hello'}
237
+ onobj={{hello: 'world'}}
238
+ onarray={['one', 'two']}
239
+ ontrue={true}
240
+ onfalse={false}
241
+ />,
242
+ container,
243
+ );
244
+ const customElement = container.querySelector('my-custom-element');
245
+ expect(customElement.getAttribute('onstring')).toBe('hello');
246
+ expect(customElement.getAttribute('onobj')).toBe('[object Object]');
247
+ expect(customElement.getAttribute('onarray')).toBe('one,two');
248
+ expect(customElement.getAttribute('ontrue')).toBe('true');
249
+ expect(customElement.getAttribute('onfalse')).toBe('false');
250
+
251
+ // Dispatch the corresponding event names to make sure that nothing crashes.
252
+ customElement.dispatchEvent(new Event('string'));
253
+ customElement.dispatchEvent(new Event('obj'));
254
+ customElement.dispatchEvent(new Event('array'));
255
+ customElement.dispatchEvent(new Event('true'));
256
+ customElement.dispatchEvent(new Event('false'));
257
+ });
258
+
259
+ it('custom elements should still have onClick treated like regular elements', () => {
260
+ let syntheticClickEvent = null;
261
+ const syntheticEventHandler = jest.fn(
262
+ event => (syntheticClickEvent = event),
263
+ );
264
+ let nativeClickEvent = null;
265
+ const nativeEventHandler = jest.fn(event => (nativeClickEvent = event));
266
+ function Test() {
267
+ return <my-custom-element onClick={syntheticEventHandler} />;
268
+ }
269
+
270
+ const container = document.createElement('div');
271
+ document.body.appendChild(container);
272
+ ReactDOM.render(<Test />, container);
273
+
274
+ const customElement = container.querySelector('my-custom-element');
275
+ customElement.onclick = nativeEventHandler;
276
+ container.querySelector('my-custom-element').click();
277
+
278
+ expect(nativeEventHandler).toHaveBeenCalledTimes(1);
279
+ expect(syntheticEventHandler).toHaveBeenCalledTimes(1);
280
+ expect(syntheticClickEvent.nativeEvent).toBe(nativeClickEvent);
281
+ });
282
+
283
+ // @gate enableCustomElementPropertySupport
284
+ it('custom elements should allow custom events with capture event listeners', () => {
285
+ const oncustomeventCapture = jest.fn();
286
+ const oncustomevent = jest.fn();
287
+ function Test() {
288
+ return (
289
+ <my-custom-element
290
+ oncustomeventCapture={oncustomeventCapture}
291
+ oncustomevent={oncustomevent}>
292
+ <div />
293
+ </my-custom-element>
294
+ );
295
+ }
296
+ const container = document.createElement('div');
297
+ ReactDOM.render(<Test />, container);
298
+ container
299
+ .querySelector('my-custom-element > div')
300
+ .dispatchEvent(new Event('customevent', {bubbles: false}));
301
+ expect(oncustomeventCapture).toHaveBeenCalledTimes(1);
302
+ expect(oncustomevent).toHaveBeenCalledTimes(0);
303
+ });
304
+
305
+ it('innerHTML should not work on custom elements', () => {
306
+ const container = document.createElement('div');
307
+ ReactDOM.render(<my-custom-element innerHTML="foo" />, container);
308
+ const customElement = container.querySelector('my-custom-element');
309
+ expect(customElement.getAttribute('innerHTML')).toBe(null);
310
+ expect(customElement.hasChildNodes()).toBe(false);
311
+
312
+ // Render again to verify the update codepath doesn't accidentally let
313
+ // something through.
314
+ ReactDOM.render(<my-custom-element innerHTML="bar" />, container);
315
+ expect(customElement.getAttribute('innerHTML')).toBe(null);
316
+ expect(customElement.hasChildNodes()).toBe(false);
317
+ });
318
+
319
+ // @gate enableCustomElementPropertySupport
320
+ it('innerText should not work on custom elements', () => {
321
+ const container = document.createElement('div');
322
+ ReactDOM.render(<my-custom-element innerText="foo" />, container);
323
+ const customElement = container.querySelector('my-custom-element');
324
+ expect(customElement.getAttribute('innerText')).toBe(null);
325
+ expect(customElement.hasChildNodes()).toBe(false);
326
+
327
+ // Render again to verify the update codepath doesn't accidentally let
328
+ // something through.
329
+ ReactDOM.render(<my-custom-element innerText="bar" />, container);
330
+ expect(customElement.getAttribute('innerText')).toBe(null);
331
+ expect(customElement.hasChildNodes()).toBe(false);
332
+ });
333
+
334
+ // @gate enableCustomElementPropertySupport
335
+ it('textContent should not work on custom elements', () => {
336
+ const container = document.createElement('div');
337
+ ReactDOM.render(<my-custom-element textContent="foo" />, container);
338
+ const customElement = container.querySelector('my-custom-element');
339
+ expect(customElement.getAttribute('textContent')).toBe(null);
340
+ expect(customElement.hasChildNodes()).toBe(false);
341
+
342
+ // Render again to verify the update codepath doesn't accidentally let
343
+ // something through.
344
+ ReactDOM.render(<my-custom-element textContent="bar" />, container);
345
+ expect(customElement.getAttribute('textContent')).toBe(null);
346
+ expect(customElement.hasChildNodes()).toBe(false);
347
+ });
348
+
349
+ // @gate enableCustomElementPropertySupport
350
+ it('values should not be converted to booleans when assigning into custom elements', () => {
351
+ const container = document.createElement('div');
352
+ document.body.appendChild(container);
353
+ ReactDOM.render(<my-custom-element />, container);
354
+ const customElement = container.querySelector('my-custom-element');
355
+ customElement.foo = null;
356
+
357
+ // true => string
358
+ ReactDOM.render(<my-custom-element foo={true} />, container);
359
+ expect(customElement.foo).toBe(true);
360
+ ReactDOM.render(<my-custom-element foo="bar" />, container);
361
+ expect(customElement.foo).toBe('bar');
362
+
363
+ // false => string
364
+ ReactDOM.render(<my-custom-element foo={false} />, container);
365
+ expect(customElement.foo).toBe(false);
366
+ ReactDOM.render(<my-custom-element foo="bar" />, container);
367
+ expect(customElement.foo).toBe('bar');
368
+
369
+ // true => null
370
+ ReactDOM.render(<my-custom-element foo={true} />, container);
371
+ expect(customElement.foo).toBe(true);
372
+ ReactDOM.render(<my-custom-element foo={null} />, container);
373
+ expect(customElement.foo).toBe(null);
374
+
375
+ // false => null
376
+ ReactDOM.render(<my-custom-element foo={false} />, container);
377
+ expect(customElement.foo).toBe(false);
378
+ ReactDOM.render(<my-custom-element foo={null} />, container);
379
+ expect(customElement.foo).toBe(null);
380
+ });
381
+
382
+ // @gate enableCustomElementPropertySupport
383
+ it('custom element custom event handlers assign multiple types', () => {
384
+ const container = document.createElement('div');
385
+ document.body.appendChild(container);
386
+ const oncustomevent = jest.fn();
387
+
388
+ // First render with string
389
+ ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
390
+ const customelement = container.querySelector('my-custom-element');
391
+ customelement.dispatchEvent(new Event('customevent'));
392
+ expect(oncustomevent).toHaveBeenCalledTimes(0);
393
+ expect(customelement.oncustomevent).toBe(undefined);
394
+ expect(customelement.getAttribute('oncustomevent')).toBe('foo');
395
+
396
+ // string => event listener
397
+ ReactDOM.render(
398
+ <my-custom-element oncustomevent={oncustomevent} />,
399
+ container,
400
+ );
401
+ customelement.dispatchEvent(new Event('customevent'));
402
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
403
+ expect(customelement.oncustomevent).toBe(undefined);
404
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
405
+
406
+ // event listener => string
407
+ ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
408
+ customelement.dispatchEvent(new Event('customevent'));
409
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
410
+ expect(customelement.oncustomevent).toBe(undefined);
411
+ expect(customelement.getAttribute('oncustomevent')).toBe('foo');
412
+
413
+ // string => nothing
414
+ ReactDOM.render(<my-custom-element />, container);
415
+ customelement.dispatchEvent(new Event('customevent'));
416
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
417
+ expect(customelement.oncustomevent).toBe(undefined);
418
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
419
+
420
+ // nothing => event listener
421
+ ReactDOM.render(
422
+ <my-custom-element oncustomevent={oncustomevent} />,
423
+ container,
424
+ );
425
+ customelement.dispatchEvent(new Event('customevent'));
426
+ expect(oncustomevent).toHaveBeenCalledTimes(2);
427
+ expect(customelement.oncustomevent).toBe(undefined);
428
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
429
+ });
430
+
431
+ // @gate enableCustomElementPropertySupport
432
+ it('custom element custom event handlers assign multiple types with setter', () => {
433
+ const container = document.createElement('div');
434
+ document.body.appendChild(container);
435
+ const oncustomevent = jest.fn();
436
+
437
+ // First render with nothing
438
+ ReactDOM.render(<my-custom-element />, container);
439
+ const customelement = container.querySelector('my-custom-element');
440
+ // Install a setter to activate the `in` heuristic
441
+ Object.defineProperty(customelement, 'oncustomevent', {
442
+ set: function(x) {
443
+ this._oncustomevent = x;
444
+ },
445
+ get: function() {
446
+ return this._oncustomevent;
447
+ },
448
+ });
449
+ expect(customelement.oncustomevent).toBe(undefined);
450
+
451
+ // nothing => event listener
452
+ ReactDOM.render(
453
+ <my-custom-element oncustomevent={oncustomevent} />,
454
+ container,
455
+ );
456
+ customelement.dispatchEvent(new Event('customevent'));
457
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
458
+ expect(customelement.oncustomevent).toBe(null);
459
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
460
+
461
+ // event listener => string
462
+ ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
463
+ customelement.dispatchEvent(new Event('customevent'));
464
+ expect(oncustomevent).toHaveBeenCalledTimes(1);
465
+ expect(customelement.oncustomevent).toBe('foo');
466
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
467
+
468
+ // string => event listener
469
+ ReactDOM.render(
470
+ <my-custom-element oncustomevent={oncustomevent} />,
471
+ container,
472
+ );
473
+ customelement.dispatchEvent(new Event('customevent'));
474
+ expect(oncustomevent).toHaveBeenCalledTimes(2);
475
+ expect(customelement.oncustomevent).toBe(null);
476
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
477
+
478
+ // event listener => nothing
479
+ ReactDOM.render(<my-custom-element />, container);
480
+ customelement.dispatchEvent(new Event('customevent'));
481
+ expect(oncustomevent).toHaveBeenCalledTimes(2);
482
+ expect(customelement.oncustomevent).toBe(null);
483
+ expect(customelement.getAttribute('oncustomevent')).toBe(null);
484
+ });
485
+
486
+ // @gate enableCustomElementPropertySupport
487
+ it('assigning to a custom element property should not remove attributes', () => {
488
+ const container = document.createElement('div');
489
+ document.body.appendChild(container);
490
+ ReactDOM.render(<my-custom-element foo="one" />, container);
491
+ const customElement = container.querySelector('my-custom-element');
492
+ expect(customElement.getAttribute('foo')).toBe('one');
493
+
494
+ // Install a setter to activate the `in` heuristic
495
+ Object.defineProperty(customElement, 'foo', {
496
+ set: function(x) {
497
+ this._foo = x;
498
+ },
499
+ get: function() {
500
+ return this._foo;
501
+ },
502
+ });
503
+ ReactDOM.render(<my-custom-element foo="two" />, container);
504
+ expect(customElement.foo).toBe('two');
505
+ expect(customElement.getAttribute('foo')).toBe('one');
506
+ });
507
});
508
509
describe('deleteValueForProperty', () => {