1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import { 16 bindEventListener, 17 elementIsEditable, 18 findRef, 19 isOrContains, 20 toHTMLElement, 21} from './dom_utils'; 22 23describe('isOrContains', () => { 24 const parent = document.createElement('div'); 25 const child = document.createElement('div'); 26 parent.appendChild(child); 27 28 it('finds child in parent', () => { 29 expect(isOrContains(parent, child)).toBeTruthy(); 30 }); 31 32 it('finds child in child', () => { 33 expect(isOrContains(child, child)).toBeTruthy(); 34 }); 35 36 it('does not find parent in child', () => { 37 expect(isOrContains(child, parent)).toBeFalsy(); 38 }); 39}); 40 41describe('findRef', () => { 42 const parent = document.createElement('div'); 43 const fooChild = document.createElement('div'); 44 fooChild.setAttribute('ref', 'foo'); 45 parent.appendChild(fooChild); 46 const barChild = document.createElement('div'); 47 barChild.setAttribute('ref', 'bar'); 48 parent.appendChild(barChild); 49 50 it('should find refs in parent divs', () => { 51 expect(findRef(parent, 'foo')).toEqual(fooChild); 52 expect(findRef(parent, 'bar')).toEqual(barChild); 53 }); 54 55 it('should find refs in self divs', () => { 56 expect(findRef(fooChild, 'foo')).toEqual(fooChild); 57 expect(findRef(barChild, 'bar')).toEqual(barChild); 58 }); 59 60 it('should fail to find ref in unrelated divs', () => { 61 const unrelated = document.createElement('div'); 62 expect(findRef(unrelated, 'foo')).toBeNull(); 63 expect(findRef(fooChild, 'bar')).toBeNull(); 64 expect(findRef(barChild, 'foo')).toBeNull(); 65 }); 66}); 67 68describe('toHTMLElement', () => { 69 it('should convert a div to an HTMLElement', () => { 70 const divElement: Element = document.createElement('div'); 71 expect(toHTMLElement(divElement)).toEqual(divElement); 72 }); 73 74 it('should fail to convert an svg element to an HTMLElement', () => { 75 const svgElement = document.createElementNS( 76 'http://www.w3.org/2000/svg', 77 'svg', 78 ); 79 expect(() => toHTMLElement(svgElement)).toThrow(Error); 80 }); 81}); 82 83describe('elementIsEditable', () => { 84 test('text input', () => { 85 const el = document.createElement('input'); 86 el.setAttribute('type', 'text'); 87 expect(elementIsEditable(el)).toBeTruthy(); 88 }); 89 90 test('radio input', () => { 91 const el = document.createElement('input'); 92 el.setAttribute('type', 'radio'); 93 expect(elementIsEditable(el)).toBeFalsy(); 94 }); 95 96 test('checkbox input', () => { 97 const el = document.createElement('input'); 98 el.setAttribute('type', 'checkbox'); 99 expect(elementIsEditable(el)).toBeFalsy(); 100 }); 101 102 test('button input', () => { 103 const el = document.createElement('input'); 104 el.setAttribute('type', 'button'); 105 expect(elementIsEditable(el)).toBeFalsy(); 106 }); 107 108 test('div', () => { 109 const el = document.createElement('div'); 110 expect(elementIsEditable(el)).toBeFalsy(); 111 }); 112 113 test('textarea', () => { 114 const el = document.createElement('textarea'); 115 expect(elementIsEditable(el)).toBeTruthy(); 116 }); 117 118 test('nested', () => { 119 const el = document.createElement('textarea'); 120 const nested = document.createElement('div'); 121 el.appendChild(nested); 122 expect(elementIsEditable(nested)).toBeTruthy(); 123 }); 124}); 125 126describe('bindEventListener', () => { 127 let element: HTMLElement; 128 let handler: jest.Mock; 129 130 beforeEach(() => { 131 element = document.createElement('div'); 132 handler = jest.fn(); 133 }); 134 135 test('adds the event listener and triggers the handler', () => { 136 const disposable = bindEventListener(element, 'click', handler); 137 138 // Simulate a click event 139 const event = new Event('click'); 140 element.dispatchEvent(event); 141 142 // Ensure the handler was called 143 expect(handler).toHaveBeenCalledWith(event); 144 145 // Dispose of the event listener 146 disposable[Symbol.dispose](); 147 148 // Reset the mock and dispatch the event again 149 handler.mockReset(); 150 element.dispatchEvent(event); 151 152 // Ensure the handler was not called after disposing 153 expect(handler).not.toHaveBeenCalled(); 154 }); 155 156 test('does not throw when disposing multiple times', () => { 157 const disposable = bindEventListener(element, 'click', handler); 158 159 expect(() => { 160 disposable[Symbol.dispose](); 161 disposable[Symbol.dispose](); 162 }).not.toThrow(); 163 }); 164}); 165