Set up comprehensive frontend testing infrastructure
- Install Jest for unit testing with React Testing Library - Install Playwright for end-to-end testing - Configure Jest with proper TypeScript support and module mapping - Create test setup files and utilities for both unit and e2e tests Components: * Jest configuration with coverage thresholds * Playwright configuration with browser automation * Unit tests for LoginForm, AuthContext, and useSocketIO hook * E2E tests for authentication, dashboard, and agents workflows * GitHub Actions workflow for automated testing * Mock data and API utilities for consistent testing * Test documentation with best practices Testing features: - Unit tests with 70% coverage threshold - E2E tests with API mocking and user journey testing - CI/CD integration for automated test runs - Cross-browser testing support with Playwright - Authentication system testing end-to-end 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										18
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/click/isClickableInput.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/click/isClickableInput.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
|  | ||||
| var clickableInputTypes = /*#__PURE__*/ function(clickableInputTypes) { | ||||
|     clickableInputTypes["button"] = "button"; | ||||
|     clickableInputTypes["color"] = "color"; | ||||
|     clickableInputTypes["file"] = "file"; | ||||
|     clickableInputTypes["image"] = "image"; | ||||
|     clickableInputTypes["reset"] = "reset"; | ||||
|     clickableInputTypes["submit"] = "submit"; | ||||
|     clickableInputTypes["checkbox"] = "checkbox"; | ||||
|     clickableInputTypes["radio"] = "radio"; | ||||
|     return clickableInputTypes; | ||||
| }(clickableInputTypes || {}); | ||||
| function isClickableInput(element) { | ||||
|     return isElementType(element, 'button') || isElementType(element, 'input') && element.type in clickableInputTypes; | ||||
| } | ||||
|  | ||||
| export { isClickableInput }; | ||||
							
								
								
									
										14
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/Blob.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/Blob.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| // jsdom does not implement Blob.text() | ||||
| function readBlobText(blob, FileReader) { | ||||
|     return new Promise((res, rej)=>{ | ||||
|         const fr = new FileReader(); | ||||
|         fr.onerror = rej; | ||||
|         fr.onabort = rej; | ||||
|         fr.onload = ()=>{ | ||||
|             res(String(fr.result)); | ||||
|         }; | ||||
|         fr.readAsText(blob); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export { readBlobText }; | ||||
							
								
								
									
										162
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/Clipboard.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/Clipboard.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| import { getWindow } from '../misc/getWindow.js'; | ||||
| import { readBlobText } from './Blob.js'; | ||||
| import { createDataTransfer, getBlobFromDataTransferItem } from './DataTransfer.js'; | ||||
|  | ||||
| // Clipboard is not available in jsdom | ||||
| function _define_property(obj, key, value) { | ||||
|     if (key in obj) { | ||||
|         Object.defineProperty(obj, key, { | ||||
|             value: value, | ||||
|             enumerable: true, | ||||
|             configurable: true, | ||||
|             writable: true | ||||
|         }); | ||||
|     } else { | ||||
|         obj[key] = value; | ||||
|     } | ||||
|     return obj; | ||||
| } | ||||
| // MDN lists string|Blob|Promise<Blob|string> as possible types in ClipboardItemData | ||||
| // lib.dom.d.ts lists only Promise<Blob|string> | ||||
| // https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem/ClipboardItem#syntax | ||||
| function createClipboardItem(window, ...blobs) { | ||||
|     const dataMap = Object.fromEntries(blobs.map((b)=>[ | ||||
|             typeof b === 'string' ? 'text/plain' : b.type, | ||||
|             Promise.resolve(b) | ||||
|         ])); | ||||
|     // use real ClipboardItem if available | ||||
|     /* istanbul ignore if */ if (typeof window.ClipboardItem !== 'undefined') { | ||||
|         return new window.ClipboardItem(dataMap); | ||||
|     } | ||||
|     return new class ClipboardItem { | ||||
|         get types() { | ||||
|             return Array.from(Object.keys(this.data)); | ||||
|         } | ||||
|         async getType(type) { | ||||
|             const value = await this.data[type]; | ||||
|             if (!value) { | ||||
|                 throw new Error(`${type} is not one of the available MIME types on this item.`); | ||||
|             } | ||||
|             return value instanceof window.Blob ? value : new window.Blob([ | ||||
|                 value | ||||
|             ], { | ||||
|                 type | ||||
|             }); | ||||
|         } | ||||
|         constructor(d){ | ||||
|             _define_property(this, "data", undefined); | ||||
|             this.data = d; | ||||
|         } | ||||
|     }(dataMap); | ||||
| } | ||||
| const ClipboardStubControl = Symbol('Manage ClipboardSub'); | ||||
| function createClipboardStub(window, control) { | ||||
|     return Object.assign(new class Clipboard extends window.EventTarget { | ||||
|         async read() { | ||||
|             return Array.from(this.items); | ||||
|         } | ||||
|         async readText() { | ||||
|             let text = ''; | ||||
|             for (const item of this.items){ | ||||
|                 const type = item.types.includes('text/plain') ? 'text/plain' : item.types.find((t)=>t.startsWith('text/')); | ||||
|                 if (type) { | ||||
|                     text += await item.getType(type).then((b)=>readBlobText(b, window.FileReader)); | ||||
|                 } | ||||
|             } | ||||
|             return text; | ||||
|         } | ||||
|         async write(data) { | ||||
|             this.items = data; | ||||
|         } | ||||
|         async writeText(text) { | ||||
|             this.items = [ | ||||
|                 createClipboardItem(window, text) | ||||
|             ]; | ||||
|         } | ||||
|         constructor(...args){ | ||||
|             super(...args), _define_property(this, "items", []); | ||||
|         } | ||||
|     }(), { | ||||
|         [ClipboardStubControl]: control | ||||
|     }); | ||||
| } | ||||
| function isClipboardStub(clipboard) { | ||||
|     return !!(clipboard === null || clipboard === undefined ? undefined : clipboard[ClipboardStubControl]); | ||||
| } | ||||
| function attachClipboardStubToView(window) { | ||||
|     if (isClipboardStub(window.navigator.clipboard)) { | ||||
|         return window.navigator.clipboard[ClipboardStubControl]; | ||||
|     } | ||||
|     const realClipboard = Object.getOwnPropertyDescriptor(window.navigator, 'clipboard'); | ||||
|     let stub; | ||||
|     const control = { | ||||
|         resetClipboardStub: ()=>{ | ||||
|             stub = createClipboardStub(window, control); | ||||
|         }, | ||||
|         detachClipboardStub: ()=>{ | ||||
|             /* istanbul ignore if */ if (realClipboard) { | ||||
|                 Object.defineProperty(window.navigator, 'clipboard', realClipboard); | ||||
|             } else { | ||||
|                 Object.defineProperty(window.navigator, 'clipboard', { | ||||
|                     value: undefined, | ||||
|                     configurable: true | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     stub = createClipboardStub(window, control); | ||||
|     Object.defineProperty(window.navigator, 'clipboard', { | ||||
|         get: ()=>stub, | ||||
|         configurable: true | ||||
|     }); | ||||
|     return stub[ClipboardStubControl]; | ||||
| } | ||||
| function resetClipboardStubOnView(window) { | ||||
|     if (isClipboardStub(window.navigator.clipboard)) { | ||||
|         window.navigator.clipboard[ClipboardStubControl].resetClipboardStub(); | ||||
|     } | ||||
| } | ||||
| function detachClipboardStubFromView(window) { | ||||
|     if (isClipboardStub(window.navigator.clipboard)) { | ||||
|         window.navigator.clipboard[ClipboardStubControl].detachClipboardStub(); | ||||
|     } | ||||
| } | ||||
| async function readDataTransferFromClipboard(document) { | ||||
|     const window = document.defaultView; | ||||
|     const clipboard = window === null || window === undefined ? undefined : window.navigator.clipboard; | ||||
|     const items = clipboard && await clipboard.read(); | ||||
|     if (!items) { | ||||
|         throw new Error('The Clipboard API is unavailable.'); | ||||
|     } | ||||
|     const dt = createDataTransfer(window); | ||||
|     for (const item of items){ | ||||
|         for (const type of item.types){ | ||||
|             dt.setData(type, await item.getType(type).then((b)=>readBlobText(b, window.FileReader))); | ||||
|         } | ||||
|     } | ||||
|     return dt; | ||||
| } | ||||
| async function writeDataTransferToClipboard(document, clipboardData) { | ||||
|     const window = getWindow(document); | ||||
|     const clipboard = window.navigator.clipboard; | ||||
|     const items = []; | ||||
|     for(let i = 0; i < clipboardData.items.length; i++){ | ||||
|         const dtItem = clipboardData.items[i]; | ||||
|         const blob = await getBlobFromDataTransferItem(window, dtItem); | ||||
|         items.push(createClipboardItem(window, blob)); | ||||
|     } | ||||
|     const written = clipboard && await clipboard.write(items).then(()=>true, // Can happen with other implementations that e.g. require permissions | ||||
|     /* istanbul ignore next */ ()=>false); | ||||
|     if (!written) { | ||||
|         throw new Error('The Clipboard API is unavailable.'); | ||||
|     } | ||||
| } | ||||
| const g = globalThis; | ||||
| /* istanbul ignore else */ if (typeof g.afterEach === 'function') { | ||||
|     g.afterEach(()=>resetClipboardStubOnView(globalThis.window)); | ||||
| } | ||||
| /* istanbul ignore else */ if (typeof g.afterAll === 'function') { | ||||
|     g.afterAll(()=>detachClipboardStubFromView(globalThis.window)); | ||||
| } | ||||
|  | ||||
| export { attachClipboardStubToView, createClipboardItem, detachClipboardStubFromView, readDataTransferFromClipboard, resetClipboardStubOnView, writeDataTransferToClipboard }; | ||||
							
								
								
									
										133
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/DataTransfer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/DataTransfer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| import { createFileList } from './FileList.js'; | ||||
|  | ||||
| function _define_property(obj, key, value) { | ||||
|     if (key in obj) { | ||||
|         Object.defineProperty(obj, key, { | ||||
|             value: value, | ||||
|             enumerable: true, | ||||
|             configurable: true, | ||||
|             writable: true | ||||
|         }); | ||||
|     } else { | ||||
|         obj[key] = value; | ||||
|     } | ||||
|     return obj; | ||||
| } | ||||
| // DataTransfer is not implemented in jsdom. | ||||
| // DataTransfer with FileList is being created by the browser on certain events. | ||||
| class DataTransferItemStub { | ||||
|     getAsFile() { | ||||
|         return this.file; | ||||
|     } | ||||
|     getAsString(callback) { | ||||
|         if (typeof this.data === 'string') { | ||||
|             callback(this.data); | ||||
|         } | ||||
|     } | ||||
|     /* istanbul ignore next */ webkitGetAsEntry() { | ||||
|         throw new Error('not implemented'); | ||||
|     } | ||||
|     constructor(dataOrFile, type){ | ||||
|         _define_property(this, "kind", undefined); | ||||
|         _define_property(this, "type", undefined); | ||||
|         _define_property(this, "file", null); | ||||
|         _define_property(this, "data", undefined); | ||||
|         if (typeof dataOrFile === 'string') { | ||||
|             this.kind = 'string'; | ||||
|             this.type = String(type); | ||||
|             this.data = dataOrFile; | ||||
|         } else { | ||||
|             this.kind = 'file'; | ||||
|             this.type = dataOrFile.type; | ||||
|             this.file = dataOrFile; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| class DataTransferItemListStub extends Array { | ||||
|     add(...args) { | ||||
|         const item = new DataTransferItemStub(args[0], args[1]); | ||||
|         this.push(item); | ||||
|         return item; | ||||
|     } | ||||
|     clear() { | ||||
|         this.splice(0, this.length); | ||||
|     } | ||||
|     remove(index) { | ||||
|         this.splice(index, 1); | ||||
|     } | ||||
| } | ||||
| function getTypeMatcher(type, exact) { | ||||
|     const [group, sub] = type.split('/'); | ||||
|     const isGroup = !sub || sub === '*'; | ||||
|     return (item)=>{ | ||||
|         return exact ? item.type === (isGroup ? group : type) : isGroup ? item.type.startsWith(`${group}/`) : item.type === group; | ||||
|     }; | ||||
| } | ||||
| function createDataTransferStub(window) { | ||||
|     return new class DataTransferStub { | ||||
|         getData(format) { | ||||
|             var _this_items_find; | ||||
|             const match = (_this_items_find = this.items.find(getTypeMatcher(format, true))) !== null && _this_items_find !== undefined ? _this_items_find : this.items.find(getTypeMatcher(format, false)); | ||||
|             let text = ''; | ||||
|             match === null || match === undefined ? undefined : match.getAsString((t)=>{ | ||||
|                 text = t; | ||||
|             }); | ||||
|             return text; | ||||
|         } | ||||
|         setData(format, data) { | ||||
|             const matchIndex = this.items.findIndex(getTypeMatcher(format, true)); | ||||
|             const item = new DataTransferItemStub(data, format); | ||||
|             if (matchIndex >= 0) { | ||||
|                 this.items.splice(matchIndex, 1, item); | ||||
|             } else { | ||||
|                 this.items.push(item); | ||||
|             } | ||||
|         } | ||||
|         clearData(format) { | ||||
|             if (format) { | ||||
|                 const matchIndex = this.items.findIndex(getTypeMatcher(format, true)); | ||||
|                 if (matchIndex >= 0) { | ||||
|                     this.items.remove(matchIndex); | ||||
|                 } | ||||
|             } else { | ||||
|                 this.items.clear(); | ||||
|             } | ||||
|         } | ||||
|         get types() { | ||||
|             const t = []; | ||||
|             if (this.files.length) { | ||||
|                 t.push('Files'); | ||||
|             } | ||||
|             this.items.forEach((i)=>t.push(i.type)); | ||||
|             Object.freeze(t); | ||||
|             return t; | ||||
|         } | ||||
|         /* istanbul ignore next */ setDragImage() {} | ||||
|         constructor(){ | ||||
|             _define_property(this, "dropEffect", 'none'); | ||||
|             _define_property(this, "effectAllowed", 'uninitialized'); | ||||
|             _define_property(this, "items", new DataTransferItemListStub()); | ||||
|             _define_property(this, "files", createFileList(window, [])); | ||||
|         } | ||||
|     }(); | ||||
| } | ||||
| function createDataTransfer(window, files = []) { | ||||
|     // Use real DataTransfer if available | ||||
|     const dt = typeof window.DataTransfer === 'undefined' ? createDataTransferStub(window) : /* istanbul ignore next */ new window.DataTransfer(); | ||||
|     Object.defineProperty(dt, 'files', { | ||||
|         get: ()=>createFileList(window, files) | ||||
|     }); | ||||
|     return dt; | ||||
| } | ||||
| async function getBlobFromDataTransferItem(window, item) { | ||||
|     if (item.kind === 'file') { | ||||
|         return item.getAsFile(); | ||||
|     } | ||||
|     return new window.Blob([ | ||||
|         await new Promise((r)=>item.getAsString(r)) | ||||
|     ], { | ||||
|         type: item.type | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export { createDataTransfer, getBlobFromDataTransferItem }; | ||||
							
								
								
									
										22
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/FileList.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/dataTransfer/FileList.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // FileList can not be created per constructor. | ||||
| function createFileList(window, files) { | ||||
|     const list = { | ||||
|         ...files, | ||||
|         length: files.length, | ||||
|         item: (index)=>list[index], | ||||
|         [Symbol.iterator]: function* nextFile() { | ||||
|             for(let i = 0; i < list.length; i++){ | ||||
|                 yield list[i]; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     list.constructor = window.FileList; | ||||
|     // guard for environments without FileList | ||||
|     /* istanbul ignore else */ if (window.FileList) { | ||||
|         Object.setPrototypeOf(list, window.FileList.prototype); | ||||
|     } | ||||
|     Object.freeze(list); | ||||
|     return list; | ||||
| } | ||||
|  | ||||
| export { createFileList }; | ||||
							
								
								
									
										15
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/isContentEditable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/isContentEditable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| //jsdom is not supporting isContentEditable | ||||
| function isContentEditable(element) { | ||||
|     return element.hasAttribute('contenteditable') && (element.getAttribute('contenteditable') == 'true' || element.getAttribute('contenteditable') == ''); | ||||
| } | ||||
| /** | ||||
|  * If a node is a contenteditable or inside one, return that element. | ||||
|  */ function getContentEditable(node) { | ||||
|     const element = getElement(node); | ||||
|     return element && (element.closest('[contenteditable=""]') || element.closest('[contenteditable="true"]')); | ||||
| } | ||||
| function getElement(node) { | ||||
|     return node.nodeType === 1 ? node : node.parentElement; | ||||
| } | ||||
|  | ||||
| export { getContentEditable, isContentEditable }; | ||||
							
								
								
									
										26
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/isEditable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/isEditable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
| import { isContentEditable } from './isContentEditable.js'; | ||||
|  | ||||
| function isEditable(element) { | ||||
|     return isEditableInputOrTextArea(element) && !element.readOnly || isContentEditable(element); | ||||
| } | ||||
| var editableInputTypes = /*#__PURE__*/ function(editableInputTypes) { | ||||
|     editableInputTypes["text"] = "text"; | ||||
|     editableInputTypes["date"] = "date"; | ||||
|     editableInputTypes["datetime-local"] = "datetime-local"; | ||||
|     editableInputTypes["email"] = "email"; | ||||
|     editableInputTypes["month"] = "month"; | ||||
|     editableInputTypes["number"] = "number"; | ||||
|     editableInputTypes["password"] = "password"; | ||||
|     editableInputTypes["search"] = "search"; | ||||
|     editableInputTypes["tel"] = "tel"; | ||||
|     editableInputTypes["time"] = "time"; | ||||
|     editableInputTypes["url"] = "url"; | ||||
|     editableInputTypes["week"] = "week"; | ||||
|     return editableInputTypes; | ||||
| }(editableInputTypes || {}); | ||||
| function isEditableInputOrTextArea(element) { | ||||
|     return isElementType(element, 'textarea') || isElementType(element, 'input') && element.type in editableInputTypes; | ||||
| } | ||||
|  | ||||
| export { isEditable, isEditableInputOrTextArea }; | ||||
							
								
								
									
										23
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/maxLength.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/maxLength.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
|  | ||||
| var maxLengthSupportedTypes = /*#__PURE__*/ function(maxLengthSupportedTypes) { | ||||
|     maxLengthSupportedTypes["email"] = "email"; | ||||
|     maxLengthSupportedTypes["password"] = "password"; | ||||
|     maxLengthSupportedTypes["search"] = "search"; | ||||
|     maxLengthSupportedTypes["telephone"] = "telephone"; | ||||
|     maxLengthSupportedTypes["text"] = "text"; | ||||
|     maxLengthSupportedTypes["url"] = "url"; | ||||
|     return maxLengthSupportedTypes; | ||||
| }(maxLengthSupportedTypes || {}); | ||||
| // can't use .maxLength property because of a jsdom bug: | ||||
| // https://github.com/jsdom/jsdom/issues/2927 | ||||
| function getMaxLength(element) { | ||||
|     var _element_getAttribute; | ||||
|     const attr = (_element_getAttribute = element.getAttribute('maxlength')) !== null && _element_getAttribute !== undefined ? _element_getAttribute : ''; | ||||
|     return /^\d+$/.test(attr) && Number(attr) >= 0 ? Number(attr) : undefined; | ||||
| } | ||||
| function supportsMaxLength(element) { | ||||
|     return isElementType(element, 'textarea') || isElementType(element, 'input') && element.type in maxLengthSupportedTypes; | ||||
| } | ||||
|  | ||||
| export { getMaxLength, supportsMaxLength }; | ||||
							
								
								
									
										57
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/setFiles.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/setFiles.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| // It is not possible to create a real FileList programmatically. | ||||
| // Therefore assigning `files` property with a programmatically created FileList results in an error. | ||||
| // Just assigning the property (as per fireEvent) breaks the interweaving with the `value` property. | ||||
| const fakeFiles = Symbol('files and value properties are mocked'); | ||||
| function restoreProperty(obj, prop, descriptor) { | ||||
|     if (descriptor) { | ||||
|         Object.defineProperty(obj, prop, descriptor); | ||||
|     } else { | ||||
|         // eslint-disable-next-line @typescript-eslint/no-dynamic-delete | ||||
|         delete obj[prop]; | ||||
|     } | ||||
| } | ||||
| function setFiles(el, files) { | ||||
|     var _el_fakeFiles; | ||||
|     (_el_fakeFiles = el[fakeFiles]) === null || _el_fakeFiles === undefined ? undefined : _el_fakeFiles.restore(); | ||||
|     const typeDescr = Object.getOwnPropertyDescriptor(el, 'type'); | ||||
|     const valueDescr = Object.getOwnPropertyDescriptor(el, 'value'); | ||||
|     const filesDescr = Object.getOwnPropertyDescriptor(el, 'files'); | ||||
|     function restore() { | ||||
|         restoreProperty(el, 'type', typeDescr); | ||||
|         restoreProperty(el, 'value', valueDescr); | ||||
|         restoreProperty(el, 'files', filesDescr); | ||||
|     } | ||||
|     el[fakeFiles] = { | ||||
|         restore | ||||
|     }; | ||||
|     Object.defineProperties(el, { | ||||
|         files: { | ||||
|             configurable: true, | ||||
|             get: ()=>files | ||||
|         }, | ||||
|         value: { | ||||
|             configurable: true, | ||||
|             get: ()=>files.length ? `C:\\fakepath\\${files[0].name}` : '', | ||||
|             set (v) { | ||||
|                 if (v === '') { | ||||
|                     restore(); | ||||
|                 } else { | ||||
|                     var _valueDescr_set; | ||||
|                     valueDescr === null || valueDescr === undefined ? undefined : (_valueDescr_set = valueDescr.set) === null || _valueDescr_set === undefined ? undefined : _valueDescr_set.call(el, v); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         type: { | ||||
|             configurable: true, | ||||
|             get: ()=>'file', | ||||
|             set (v) { | ||||
|                 if (v !== 'file') { | ||||
|                     restore(); | ||||
|                     el.type = v; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export { setFiles }; | ||||
							
								
								
									
										37
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/timeValue.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/edit/timeValue.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| const parseInt = globalThis.parseInt; | ||||
| function buildTimeValue(value) { | ||||
|     const onlyDigitsValue = value.replace(/\D/g, ''); | ||||
|     if (onlyDigitsValue.length < 2) { | ||||
|         return value; | ||||
|     } | ||||
|     const firstDigit = parseInt(onlyDigitsValue[0], 10); | ||||
|     const secondDigit = parseInt(onlyDigitsValue[1], 10); | ||||
|     if (firstDigit >= 3 || firstDigit === 2 && secondDigit >= 4) { | ||||
|         let index; | ||||
|         if (firstDigit >= 3) { | ||||
|             index = 1; | ||||
|         } else { | ||||
|             index = 2; | ||||
|         } | ||||
|         return build(onlyDigitsValue, index); | ||||
|     } | ||||
|     if (value.length === 2) { | ||||
|         return value; | ||||
|     } | ||||
|     return build(onlyDigitsValue, 2); | ||||
| } | ||||
| function build(onlyDigitsValue, index) { | ||||
|     const hours = onlyDigitsValue.slice(0, index); | ||||
|     const validHours = Math.min(parseInt(hours, 10), 23); | ||||
|     const minuteCharacters = onlyDigitsValue.slice(index); | ||||
|     const parsedMinutes = parseInt(minuteCharacters, 10); | ||||
|     const validMinutes = Math.min(parsedMinutes, 59); | ||||
|     return `${validHours.toString().padStart(2, '0')}:${validMinutes.toString().padStart(2, '0')}`; | ||||
| } | ||||
| function isValidDateOrTimeValue(element, value) { | ||||
|     const clone = element.cloneNode(); | ||||
|     clone.value = value; | ||||
|     return clone.value === value; | ||||
| } | ||||
|  | ||||
| export { buildTimeValue, isValidDateOrTimeValue }; | ||||
							
								
								
									
										124
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/cursor.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/cursor.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| import { isContentEditable } from '../edit/isContentEditable.js'; | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
|  | ||||
| function getNextCursorPosition(node, offset, direction, inputType) { | ||||
|     // The behavior at text node zero offset is inconsistent. | ||||
|     // When walking backwards: | ||||
|     // Firefox always moves to zero offset and jumps over last offset. | ||||
|     // Chrome jumps over zero offset per default but over last offset when Shift is pressed. | ||||
|     // The cursor always moves to zero offset if the focus area (contenteditable or body) ends there. | ||||
|     // When walking forward both ignore zero offset. | ||||
|     // When walking over input elements the cursor moves before or after that element. | ||||
|     // When walking over line breaks the cursor moves inside any following text node. | ||||
|     if (isTextNode(node) && offset + direction >= 0 && offset + direction <= node.nodeValue.length) { | ||||
|         return { | ||||
|             node, | ||||
|             offset: offset + direction | ||||
|         }; | ||||
|     } | ||||
|     const nextNode = getNextCharacterContentNode(node, offset, direction); | ||||
|     if (nextNode) { | ||||
|         if (isTextNode(nextNode)) { | ||||
|             return { | ||||
|                 node: nextNode, | ||||
|                 offset: direction > 0 ? Math.min(1, nextNode.nodeValue.length) : Math.max(nextNode.nodeValue.length - 1, 0) | ||||
|             }; | ||||
|         } else if (isElementType(nextNode, 'br')) { | ||||
|             const nextPlusOne = getNextCharacterContentNode(nextNode, undefined, direction); | ||||
|             if (!nextPlusOne) { | ||||
|                 // The behavior when there is no possible cursor position beyond the line break is inconsistent. | ||||
|                 // In Chrome outside of contenteditable moving before a leading line break is possible. | ||||
|                 // A leading line break can still be removed per deleteContentBackward. | ||||
|                 // A trailing line break on the other hand is not removed by deleteContentForward. | ||||
|                 if (direction < 0 && inputType === 'deleteContentBackward') { | ||||
|                     return { | ||||
|                         node: nextNode.parentNode, | ||||
|                         offset: getOffset(nextNode) | ||||
|                     }; | ||||
|                 } | ||||
|                 return undefined; | ||||
|             } else if (isTextNode(nextPlusOne)) { | ||||
|                 return { | ||||
|                     node: nextPlusOne, | ||||
|                     offset: direction > 0 ? 0 : nextPlusOne.nodeValue.length | ||||
|                 }; | ||||
|             } else if (direction < 0 && isElementType(nextPlusOne, 'br')) { | ||||
|                 return { | ||||
|                     node: nextNode.parentNode, | ||||
|                     offset: getOffset(nextNode) | ||||
|                 }; | ||||
|             } else { | ||||
|                 return { | ||||
|                     node: nextPlusOne.parentNode, | ||||
|                     offset: getOffset(nextPlusOne) + (direction > 0 ? 0 : 1) | ||||
|                 }; | ||||
|             } | ||||
|         } else { | ||||
|             return { | ||||
|                 node: nextNode.parentNode, | ||||
|                 offset: getOffset(nextNode) + (direction > 0 ? 1 : 0) | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| function getNextCharacterContentNode(node, offset, direction) { | ||||
|     const nextOffset = Number(offset) + (direction < 0 ? -1 : 0); | ||||
|     if (offset !== undefined && isElement(node) && nextOffset >= 0 && nextOffset < node.children.length) { | ||||
|         node = node.children[nextOffset]; | ||||
|     } | ||||
|     return walkNodes(node, direction === 1 ? 'next' : 'previous', isTreatedAsCharacterContent); | ||||
| } | ||||
| function isTreatedAsCharacterContent(node) { | ||||
|     if (isTextNode(node)) { | ||||
|         return true; | ||||
|     } | ||||
|     if (isElement(node)) { | ||||
|         if (isElementType(node, [ | ||||
|             'input', | ||||
|             'textarea' | ||||
|         ])) { | ||||
|             return node.type !== 'hidden'; | ||||
|         } else if (isElementType(node, 'br')) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| function getOffset(node) { | ||||
|     let i = 0; | ||||
|     while(node.previousSibling){ | ||||
|         i++; | ||||
|         node = node.previousSibling; | ||||
|     } | ||||
|     return i; | ||||
| } | ||||
| function isElement(node) { | ||||
|     return node.nodeType === 1; | ||||
| } | ||||
| function isTextNode(node) { | ||||
|     return node.nodeType === 3; | ||||
| } | ||||
| function walkNodes(node, direction, callback) { | ||||
|     for(;;){ | ||||
|         var _node_ownerDocument; | ||||
|         const sibling = node[`${direction}Sibling`]; | ||||
|         if (sibling) { | ||||
|             node = getDescendant(sibling, direction === 'next' ? 'first' : 'last'); | ||||
|             if (callback(node)) { | ||||
|                 return node; | ||||
|             } | ||||
|         } else if (node.parentNode && (!isElement(node.parentNode) || !isContentEditable(node.parentNode) && node.parentNode !== ((_node_ownerDocument = node.ownerDocument) === null || _node_ownerDocument === undefined ? undefined : _node_ownerDocument.body))) { | ||||
|             node = node.parentNode; | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| function getDescendant(node, direction) { | ||||
|     while(node.hasChildNodes()){ | ||||
|         node = node[`${direction}Child`]; | ||||
|     } | ||||
|     return node; | ||||
| } | ||||
|  | ||||
| export { getNextCursorPosition }; | ||||
							
								
								
									
										20
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/getActiveElement.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/getActiveElement.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import { isDisabled } from '../misc/isDisabled.js'; | ||||
|  | ||||
| function getActiveElement(document) { | ||||
|     const activeElement = document.activeElement; | ||||
|     if (activeElement === null || activeElement === undefined ? undefined : activeElement.shadowRoot) { | ||||
|         return getActiveElement(activeElement.shadowRoot); | ||||
|     } else { | ||||
|         // Browser does not yield disabled elements as document.activeElement - jsdom does | ||||
|         if (isDisabled(activeElement)) { | ||||
|             return document.ownerDocument ? /* istanbul ignore next */ document.ownerDocument.body : document.body; | ||||
|         } | ||||
|         return activeElement; | ||||
|     } | ||||
| } | ||||
| function getActiveElementOrBody(document) { | ||||
|     var _getActiveElement; | ||||
|     return (_getActiveElement = getActiveElement(document)) !== null && _getActiveElement !== undefined ? _getActiveElement : /* istanbul ignore next */ document.body; | ||||
| } | ||||
|  | ||||
| export { getActiveElement, getActiveElementOrBody }; | ||||
							
								
								
									
										78
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/getTabDestination.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/getTabDestination.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| import { isDisabled } from '../misc/isDisabled.js'; | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
| import { isVisible } from '../misc/isVisible.js'; | ||||
| import { FOCUSABLE_SELECTOR } from './selector.js'; | ||||
|  | ||||
| function getTabDestination(activeElement, shift) { | ||||
|     const document = activeElement.ownerDocument; | ||||
|     const focusableElements = document.querySelectorAll(FOCUSABLE_SELECTOR); | ||||
|     const enabledElements = Array.from(focusableElements).filter((el)=>el === activeElement || !(Number(el.getAttribute('tabindex')) < 0 || isDisabled(el))); | ||||
|     // tabindex has no effect if the active element has negative tabindex | ||||
|     if (Number(activeElement.getAttribute('tabindex')) >= 0) { | ||||
|         enabledElements.sort((a, b)=>{ | ||||
|             const i = Number(a.getAttribute('tabindex')); | ||||
|             const j = Number(b.getAttribute('tabindex')); | ||||
|             if (i === j) { | ||||
|                 return 0; | ||||
|             } else if (i === 0) { | ||||
|                 return 1; | ||||
|             } else if (j === 0) { | ||||
|                 return -1; | ||||
|             } | ||||
|             return i - j; | ||||
|         }); | ||||
|     } | ||||
|     const checkedRadio = {}; | ||||
|     let prunedElements = [ | ||||
|         document.body | ||||
|     ]; | ||||
|     const activeRadioGroup = isElementType(activeElement, 'input', { | ||||
|         type: 'radio' | ||||
|     }) ? activeElement.name : undefined; | ||||
|     enabledElements.forEach((currentElement)=>{ | ||||
|         const el = currentElement; | ||||
|         // For radio groups keep only the active radio | ||||
|         // If there is no active radio, keep only the checked radio | ||||
|         // If there is no checked radio, treat like everything else | ||||
|         if (isElementType(el, 'input', { | ||||
|             type: 'radio' | ||||
|         }) && el.name) { | ||||
|             // If the active element is part of the group, add only that | ||||
|             if (el === activeElement) { | ||||
|                 prunedElements.push(el); | ||||
|                 return; | ||||
|             } else if (el.name === activeRadioGroup) { | ||||
|                 return; | ||||
|             } | ||||
|             // If we stumble upon a checked radio, remove the others | ||||
|             if (el.checked) { | ||||
|                 prunedElements = prunedElements.filter((e)=>!isElementType(e, 'input', { | ||||
|                         type: 'radio', | ||||
|                         name: el.name | ||||
|                     })); | ||||
|                 prunedElements.push(el); | ||||
|                 checkedRadio[el.name] = el; | ||||
|                 return; | ||||
|             } | ||||
|             // If we already found the checked one, skip | ||||
|             if (typeof checkedRadio[el.name] !== 'undefined') { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         prunedElements.push(el); | ||||
|     }); | ||||
|     for(let index = prunedElements.findIndex((el)=>el === activeElement);;){ | ||||
|         index += shift ? -1 : 1; | ||||
|         // loop at overflow | ||||
|         if (index === prunedElements.length) { | ||||
|             index = 0; | ||||
|         } else if (index === -1) { | ||||
|             index = prunedElements.length - 1; | ||||
|         } | ||||
|         if (prunedElements[index] === activeElement || prunedElements[index] === document.body || isVisible(prunedElements[index])) { | ||||
|             return prunedElements[index]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| export { getTabDestination }; | ||||
							
								
								
									
										7
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/isFocusable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/isFocusable.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { FOCUSABLE_SELECTOR } from './selector.js'; | ||||
|  | ||||
| function isFocusable(element) { | ||||
|     return element.matches(FOCUSABLE_SELECTOR); | ||||
| } | ||||
|  | ||||
| export { isFocusable }; | ||||
							
								
								
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/selection.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/selection.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import { isClickableInput } from '../click/isClickableInput.js'; | ||||
| import { isEditableInputOrTextArea } from '../edit/isEditable.js'; | ||||
|  | ||||
| /** | ||||
|  * Determine if the element has its own selection implementation | ||||
|  * and does not interact with the Document Selection API. | ||||
|  */ function hasOwnSelection(node) { | ||||
|     return isElement(node) && isEditableInputOrTextArea(node); | ||||
| } | ||||
| function hasNoSelection(node) { | ||||
|     return isElement(node) && isClickableInput(node); | ||||
| } | ||||
| function isElement(node) { | ||||
|     return node.nodeType === 1; | ||||
| } | ||||
|  | ||||
| export { hasNoSelection, hasOwnSelection }; | ||||
							
								
								
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/selector.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/focus/selector.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| const FOCUSABLE_SELECTOR = [ | ||||
|     'input:not([type=hidden]):not([disabled])', | ||||
|     'button:not([disabled])', | ||||
|     'select:not([disabled])', | ||||
|     'textarea:not([disabled])', | ||||
|     '[contenteditable=""]', | ||||
|     '[contenteditable="true"]', | ||||
|     'a[href]', | ||||
|     '[tabindex]:not([disabled])' | ||||
| ].join(', '); | ||||
|  | ||||
| export { FOCUSABLE_SELECTOR }; | ||||
							
								
								
									
										29
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| export { isClickableInput } from './click/isClickableInput.js'; | ||||
| export { readBlobText } from './dataTransfer/Blob.js'; | ||||
| export { createDataTransfer, getBlobFromDataTransferItem } from './dataTransfer/DataTransfer.js'; | ||||
| export { createFileList } from './dataTransfer/FileList.js'; | ||||
| export { attachClipboardStubToView, createClipboardItem, detachClipboardStubFromView, readDataTransferFromClipboard, resetClipboardStubOnView, writeDataTransferToClipboard } from './dataTransfer/Clipboard.js'; | ||||
| export { buildTimeValue, isValidDateOrTimeValue } from './edit/timeValue.js'; | ||||
| export { getContentEditable, isContentEditable } from './edit/isContentEditable.js'; | ||||
| export { isEditable, isEditableInputOrTextArea } from './edit/isEditable.js'; | ||||
| export { getMaxLength, supportsMaxLength } from './edit/maxLength.js'; | ||||
| export { setFiles } from './edit/setFiles.js'; | ||||
| export { getNextCursorPosition } from './focus/cursor.js'; | ||||
| export { getActiveElement, getActiveElementOrBody } from './focus/getActiveElement.js'; | ||||
| export { getTabDestination } from './focus/getTabDestination.js'; | ||||
| export { isFocusable } from './focus/isFocusable.js'; | ||||
| export { hasNoSelection, hasOwnSelection } from './focus/selection.js'; | ||||
| export { FOCUSABLE_SELECTOR } from './focus/selector.js'; | ||||
| export { readNextDescriptor } from './keyDef/readNextDescriptor.js'; | ||||
| export { cloneEvent } from './misc/cloneEvent.js'; | ||||
| export { findClosest } from './misc/findClosest.js'; | ||||
| export { getDocumentFromNode } from './misc/getDocumentFromNode.js'; | ||||
| export { getTreeDiff } from './misc/getTreeDiff.js'; | ||||
| export { getWindow } from './misc/getWindow.js'; | ||||
| export { isDescendantOrSelf } from './misc/isDescendantOrSelf.js'; | ||||
| export { isElementType } from './misc/isElementType.js'; | ||||
| export { isVisible } from './misc/isVisible.js'; | ||||
| export { isDisabled } from './misc/isDisabled.js'; | ||||
| export { ApiLevel, getLevelRef, setLevelRef } from './misc/level.js'; | ||||
| export { wait } from './misc/wait.js'; | ||||
| export { assertPointerEvents, hasPointerEvents } from './pointer/cssPointerEvents.js'; | ||||
							
								
								
									
										90
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/keyDef/readNextDescriptor.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/keyDef/readNextDescriptor.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| var bracketDict = /*#__PURE__*/ function(bracketDict) { | ||||
|     bracketDict["{"] = "}"; | ||||
|     bracketDict["["] = "]"; | ||||
|     return bracketDict; | ||||
| }(bracketDict || {}); | ||||
| /** | ||||
|  * Read the next key definition from user input | ||||
|  * | ||||
|  * Describe key per `{descriptor}` or `[descriptor]`. | ||||
|  * Everything else will be interpreted as a single character as descriptor - e.g. `a`. | ||||
|  * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`. | ||||
|  * A previously pressed key can be released per `{/descriptor}`. | ||||
|  * Keeping the key pressed can be written as `{descriptor>}`. | ||||
|  * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`. | ||||
|  * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key. | ||||
|  */ function readNextDescriptor(text, context) { | ||||
|     let pos = 0; | ||||
|     const startBracket = text[pos] in bracketDict ? text[pos] : ''; | ||||
|     pos += startBracket.length; | ||||
|     const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text); | ||||
|     const type = isEscapedChar ? '' : startBracket; | ||||
|     return { | ||||
|         type, | ||||
|         ...type === '' ? readPrintableChar(text, pos, context) : readTag(text, pos, type, context) | ||||
|     }; | ||||
| } | ||||
| function readPrintableChar(text, pos, context) { | ||||
|     const descriptor = text[pos]; | ||||
|     assertDescriptor(descriptor, text, pos, context); | ||||
|     pos += descriptor.length; | ||||
|     return { | ||||
|         consumedLength: pos, | ||||
|         descriptor, | ||||
|         releasePrevious: false, | ||||
|         releaseSelf: true, | ||||
|         repeat: 1 | ||||
|     }; | ||||
| } | ||||
| function readTag(text, pos, startBracket, context) { | ||||
|     var _text_slice_match, _text_slice_match1; | ||||
|     const releasePreviousModifier = text[pos] === '/' ? '/' : ''; | ||||
|     pos += releasePreviousModifier.length; | ||||
|     const escapedDescriptor = startBracket === '{' && text[pos] === '\\'; | ||||
|     pos += Number(escapedDescriptor); | ||||
|     const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === undefined ? undefined : _text_slice_match[0]; | ||||
|     assertDescriptor(descriptor, text, pos, context); | ||||
|     pos += descriptor.length; | ||||
|     var _text_slice_match_; | ||||
|     const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === undefined ? undefined : _text_slice_match1[0]) !== null && _text_slice_match_ !== undefined ? _text_slice_match_ : ''; | ||||
|     pos += repeatModifier.length; | ||||
|     const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : ''; | ||||
|     pos += releaseSelfModifier.length; | ||||
|     const expectedEndBracket = bracketDict[startBracket]; | ||||
|     const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : ''; | ||||
|     if (!endBracket) { | ||||
|         throw new Error(getErrorMessage([ | ||||
|             !repeatModifier && 'repeat modifier', | ||||
|             !releaseSelfModifier && 'release modifier', | ||||
|             `"${expectedEndBracket}"` | ||||
|         ].filter(Boolean).join(' or '), text[pos], text, context)); | ||||
|     } | ||||
|     pos += endBracket.length; | ||||
|     return { | ||||
|         consumedLength: pos, | ||||
|         descriptor, | ||||
|         releasePrevious: !!releasePreviousModifier, | ||||
|         repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1, | ||||
|         releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier) | ||||
|     }; | ||||
| } | ||||
| function assertDescriptor(descriptor, text, pos, context) { | ||||
|     if (!descriptor) { | ||||
|         throw new Error(getErrorMessage('key descriptor', text[pos], text, context)); | ||||
|     } | ||||
| } | ||||
| function hasReleaseSelf(releaseSelfModifier, repeatModifier) { | ||||
|     if (releaseSelfModifier) { | ||||
|         return releaseSelfModifier === '/'; | ||||
|     } | ||||
|     if (repeatModifier) { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| function getErrorMessage(expected, found, text, context) { | ||||
|     return `Expected ${expected} but found "${found !== null && found !== undefined ? found : ''}" in "${text}" | ||||
|     See ${context === 'pointer' ? `https://testing-library.com/docs/user-event/pointer#pressing-a-button-or-touching-the-screen` : `https://testing-library.com/docs/user-event/keyboard`} | ||||
|     for more information about how userEvent parses your input.`; | ||||
| } | ||||
|  | ||||
| export { readNextDescriptor }; | ||||
							
								
								
									
										5
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/cloneEvent.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/cloneEvent.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| function cloneEvent(event) { | ||||
|     return new event.constructor(event.type, event); | ||||
| } | ||||
|  | ||||
| export { cloneEvent }; | ||||
							
								
								
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/findClosest.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/findClosest.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| function findClosest(element, callback) { | ||||
|     let el = element; | ||||
|     do { | ||||
|         if (callback(el)) { | ||||
|             return el; | ||||
|         } | ||||
|         el = el.parentElement; | ||||
|     }while (el && el !== element.ownerDocument.body) | ||||
|     return undefined; | ||||
| } | ||||
|  | ||||
| export { findClosest }; | ||||
							
								
								
									
										8
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getDocumentFromNode.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getDocumentFromNode.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| function getDocumentFromNode(el) { | ||||
|     return isDocument(el) ? el : el.ownerDocument; | ||||
| } | ||||
| function isDocument(node) { | ||||
|     return node.nodeType === 9; | ||||
| } | ||||
|  | ||||
| export { getDocumentFromNode }; | ||||
							
								
								
									
										23
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getTreeDiff.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getTreeDiff.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| function getTreeDiff(a, b) { | ||||
|     const treeA = []; | ||||
|     for(let el = a; el; el = el.parentElement){ | ||||
|         treeA.push(el); | ||||
|     } | ||||
|     const treeB = []; | ||||
|     for(let el = b; el; el = el.parentElement){ | ||||
|         treeB.push(el); | ||||
|     } | ||||
|     let i = 0; | ||||
|     for(;; i++){ | ||||
|         if (i >= treeA.length || i >= treeB.length || treeA[treeA.length - 1 - i] !== treeB[treeB.length - 1 - i]) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return [ | ||||
|         treeA.slice(0, treeA.length - i), | ||||
|         treeB.slice(0, treeB.length - i), | ||||
|         treeB.slice(treeB.length - i) | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| export { getTreeDiff }; | ||||
							
								
								
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getWindow.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/getWindow.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| function getWindow(node) { | ||||
|     var _node_ownerDocument; | ||||
|     if (isDocument(node) && node.defaultView) { | ||||
|         return node.defaultView; | ||||
|     } else if ((_node_ownerDocument = node.ownerDocument) === null || _node_ownerDocument === undefined ? undefined : _node_ownerDocument.defaultView) { | ||||
|         return node.ownerDocument.defaultView; | ||||
|     } | ||||
|     throw new Error(`Could not determine window of node. Node was ${describe(node)}`); | ||||
| } | ||||
| function isDocument(node) { | ||||
|     return node.nodeType === 9; | ||||
| } | ||||
| function describe(val) { | ||||
|     return typeof val === 'function' ? `function ${val.name}` : val === null ? 'null' : String(val); | ||||
| } | ||||
|  | ||||
| export { getWindow }; | ||||
							
								
								
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isDescendantOrSelf.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isDescendantOrSelf.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| function isDescendantOrSelf(potentialDescendant, potentialAncestor) { | ||||
|     let el = potentialDescendant; | ||||
|     do { | ||||
|         if (el === potentialAncestor) { | ||||
|             return true; | ||||
|         } | ||||
|         el = el.parentElement; | ||||
|     }while (el) | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| export { isDescendantOrSelf }; | ||||
							
								
								
									
										31
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isDisabled.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isDisabled.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { isElementType } from './isElementType.js'; | ||||
|  | ||||
| // This should probably just rely on the :disabled pseudo-class, but JSDOM doesn't implement it properly. | ||||
| function isDisabled(element) { | ||||
|     for(let el = element; el; el = el.parentElement){ | ||||
|         if (isElementType(el, [ | ||||
|             'button', | ||||
|             'input', | ||||
|             'select', | ||||
|             'textarea', | ||||
|             'optgroup', | ||||
|             'option' | ||||
|         ])) { | ||||
|             if (el.hasAttribute('disabled')) { | ||||
|                 return true; | ||||
|             } | ||||
|         } else if (isElementType(el, 'fieldset')) { | ||||
|             var _el_querySelector; | ||||
|             if (el.hasAttribute('disabled') && !((_el_querySelector = el.querySelector(':scope > legend')) === null || _el_querySelector === undefined ? undefined : _el_querySelector.contains(element))) { | ||||
|                 return true; | ||||
|             } | ||||
|         } else if (el.tagName.includes('-')) { | ||||
|             if (el.constructor.formAssociated && el.hasAttribute('disabled')) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| export { isDisabled }; | ||||
							
								
								
									
										18
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isElementType.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isElementType.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| function isElementType(element, tag, props) { | ||||
|     if (element.namespaceURI && element.namespaceURI !== 'http://www.w3.org/1999/xhtml') { | ||||
|         return false; | ||||
|     } | ||||
|     tag = Array.isArray(tag) ? tag : [ | ||||
|         tag | ||||
|     ]; | ||||
|     // tagName is uppercase in HTMLDocument and lowercase in XMLDocument | ||||
|     if (!tag.includes(element.tagName.toLowerCase())) { | ||||
|         return false; | ||||
|     } | ||||
|     if (props) { | ||||
|         return Object.entries(props).every(([k, v])=>element[k] === v); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| export { isElementType }; | ||||
							
								
								
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isVisible.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/isVisible.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import { getWindow } from './getWindow.js'; | ||||
|  | ||||
| function isVisible(element) { | ||||
|     const window = getWindow(element); | ||||
|     for(let el = element; el === null || el === undefined ? undefined : el.ownerDocument; el = el.parentElement){ | ||||
|         const { display, visibility } = window.getComputedStyle(el); | ||||
|         if (display === 'none') { | ||||
|             return false; | ||||
|         } | ||||
|         if (visibility === 'hidden') { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| export { isVisible }; | ||||
							
								
								
									
										13
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/level.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/level.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| var ApiLevel = /*#__PURE__*/ function(ApiLevel) { | ||||
|     ApiLevel[ApiLevel["Trigger"] = 2] = "Trigger"; | ||||
|     ApiLevel[ApiLevel["Call"] = 1] = "Call"; | ||||
|     return ApiLevel; | ||||
| }({}); | ||||
| function setLevelRef(instance, level) { | ||||
|     instance.levelRefs[level] = {}; | ||||
| } | ||||
| function getLevelRef(instance, level) { | ||||
|     return instance.levelRefs[level]; | ||||
| } | ||||
|  | ||||
| export { ApiLevel, getLevelRef, setLevelRef }; | ||||
							
								
								
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/wait.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/misc/wait.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| function wait(config) { | ||||
|     const delay = config.delay; | ||||
|     if (typeof delay !== 'number') { | ||||
|         return; | ||||
|     } | ||||
|     return Promise.all([ | ||||
|         new Promise((resolve)=>globalThis.setTimeout(()=>resolve(), delay)), | ||||
|         config.advanceTimers(delay) | ||||
|     ]); | ||||
| } | ||||
|  | ||||
| export { wait }; | ||||
							
								
								
									
										100
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/pointer/cssPointerEvents.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								frontend/node_modules/@testing-library/user-event/dist/esm/utils/pointer/cssPointerEvents.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import { PointerEventsCheckLevel } from '../../options.js'; | ||||
| import { getWindow } from '../misc/getWindow.js'; | ||||
| import { isElementType } from '../misc/isElementType.js'; | ||||
| import { ApiLevel, getLevelRef } from '../misc/level.js'; | ||||
|  | ||||
| function hasPointerEvents(instance, element) { | ||||
|     var _checkPointerEvents; | ||||
|     return ((_checkPointerEvents = checkPointerEvents(instance, element)) === null || _checkPointerEvents === undefined ? undefined : _checkPointerEvents.pointerEvents) !== 'none'; | ||||
| } | ||||
| function closestPointerEventsDeclaration(element) { | ||||
|     const window = getWindow(element); | ||||
|     for(let el = element, tree = []; el === null || el === undefined ? undefined : el.ownerDocument; el = el.parentElement){ | ||||
|         tree.push(el); | ||||
|         const pointerEvents = window.getComputedStyle(el).pointerEvents; | ||||
|         if (pointerEvents && ![ | ||||
|             'inherit', | ||||
|             'unset' | ||||
|         ].includes(pointerEvents)) { | ||||
|             return { | ||||
|                 pointerEvents, | ||||
|                 tree | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return undefined; | ||||
| } | ||||
| const PointerEventsCheck = Symbol('Last check for pointer-events'); | ||||
| function checkPointerEvents(instance, element) { | ||||
|     const lastCheck = element[PointerEventsCheck]; | ||||
|     const needsCheck = instance.config.pointerEventsCheck !== PointerEventsCheckLevel.Never && (!lastCheck || hasBitFlag(instance.config.pointerEventsCheck, PointerEventsCheckLevel.EachApiCall) && lastCheck[ApiLevel.Call] !== getLevelRef(instance, ApiLevel.Call) || hasBitFlag(instance.config.pointerEventsCheck, PointerEventsCheckLevel.EachTrigger) && lastCheck[ApiLevel.Trigger] !== getLevelRef(instance, ApiLevel.Trigger)); | ||||
|     if (!needsCheck) { | ||||
|         return lastCheck === null || lastCheck === undefined ? undefined : lastCheck.result; | ||||
|     } | ||||
|     const declaration = closestPointerEventsDeclaration(element); | ||||
|     element[PointerEventsCheck] = { | ||||
|         [ApiLevel.Call]: getLevelRef(instance, ApiLevel.Call), | ||||
|         [ApiLevel.Trigger]: getLevelRef(instance, ApiLevel.Trigger), | ||||
|         result: declaration | ||||
|     }; | ||||
|     return declaration; | ||||
| } | ||||
| function assertPointerEvents(instance, element) { | ||||
|     const declaration = checkPointerEvents(instance, element); | ||||
|     if ((declaration === null || declaration === undefined ? undefined : declaration.pointerEvents) === 'none') { | ||||
|         throw new Error([ | ||||
|             `Unable to perform pointer interaction as the element ${declaration.tree.length > 1 ? 'inherits' : 'has'} \`pointer-events: none\`:`, | ||||
|             '', | ||||
|             printTree(declaration.tree) | ||||
|         ].join('\n')); | ||||
|     } | ||||
| } | ||||
| function printTree(tree) { | ||||
|     return tree.reverse().map((el, i)=>[ | ||||
|             ''.padEnd(i), | ||||
|             el.tagName, | ||||
|             el.id && `#${el.id}`, | ||||
|             el.hasAttribute('data-testid') && `(testId=${el.getAttribute('data-testid')})`, | ||||
|             getLabelDescr(el), | ||||
|             tree.length > 1 && i === 0 && '  <-- This element declared `pointer-events: none`', | ||||
|             tree.length > 1 && i === tree.length - 1 && '  <-- Asserted pointer events here' | ||||
|         ].filter(Boolean).join('')).join('\n'); | ||||
| } | ||||
| function getLabelDescr(element) { | ||||
|     var _element_labels; | ||||
|     let label; | ||||
|     if (element.hasAttribute('aria-label')) { | ||||
|         label = element.getAttribute('aria-label'); | ||||
|     } else if (element.hasAttribute('aria-labelledby')) { | ||||
|         var _element_ownerDocument_getElementById_textContent, _element_ownerDocument_getElementById; | ||||
|         label = (_element_ownerDocument_getElementById = element.ownerDocument.getElementById(element.getAttribute('aria-labelledby'))) === null || _element_ownerDocument_getElementById === undefined ? undefined : (_element_ownerDocument_getElementById_textContent = _element_ownerDocument_getElementById.textContent) === null || _element_ownerDocument_getElementById_textContent === undefined ? undefined : _element_ownerDocument_getElementById_textContent.trim(); | ||||
|     } else if (isElementType(element, [ | ||||
|         'button', | ||||
|         'input', | ||||
|         'meter', | ||||
|         'output', | ||||
|         'progress', | ||||
|         'select', | ||||
|         'textarea' | ||||
|     ]) && ((_element_labels = element.labels) === null || _element_labels === undefined ? undefined : _element_labels.length)) { | ||||
|         label = Array.from(element.labels).map((el)=>{ | ||||
|             var _el_textContent; | ||||
|             return (_el_textContent = el.textContent) === null || _el_textContent === undefined ? undefined : _el_textContent.trim(); | ||||
|         }).join('|'); | ||||
|     } else if (isElementType(element, 'button')) { | ||||
|         var _element_textContent; | ||||
|         label = (_element_textContent = element.textContent) === null || _element_textContent === undefined ? undefined : _element_textContent.trim(); | ||||
|     } | ||||
|     label = label === null || label === undefined ? undefined : label.replace(/\n/g, '  '); | ||||
|     if (Number(label === null || label === undefined ? undefined : label.length) > 30) { | ||||
|         label = `${label === null || label === undefined ? undefined : label.substring(0, 29)}…`; | ||||
|     } | ||||
|     return label ? `(label=${label})` : ''; | ||||
| } | ||||
| // With the eslint rule and prettier the bitwise operation isn't nice to read | ||||
| function hasBitFlag(conf, flag) { | ||||
|     // eslint-disable-next-line no-bitwise | ||||
|     return (conf & flag) > 0; | ||||
| } | ||||
|  | ||||
| export { assertPointerEvents, hasPointerEvents }; | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins