├── .gitignore ├── README.md ├── app.js ├── examples ├── amazon_annotated.png ├── amazon_raw.png ├── github_annotated.png ├── github_raw.png ├── wikipedia_annotated.png ├── wikipedia_raw.png ├── wsj_annotated.png └── wsj_raw.png ├── package-lock.json ├── package.json └── playwright.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /playwright/.cache/ 5 | /screenshots -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A tool to create semantic segmentations of screenshots of websites from their DOM 2 | 3 | Examples: 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const { chromium } = require('playwright') 2 | const { createCanvas, loadImage } = require("canvas") 3 | const fs = require('fs'); 4 | 5 | // TODO 6 | // make JSON dump 7 | // add clickable field 8 | // Support all input types as classes 9 | // classes for specific elements: image would have caption, URL, etc. button would have text, URL, etc. 10 | // support for article and section 11 | // use clip to determine caption tag for nearby images 12 | 13 | // const url = 'https://www.wsj.com' 14 | // const url = 'https://en.wikipedia.org/wiki/Machine_learning' 15 | const url = 'https://github.com/openai' 16 | // const url = 'https://www.amazon.com/' 17 | 18 | // Size of the browser viewport and final images 19 | const viewportHeight = 720 20 | const viewportWidth = 1280 21 | 22 | // Categories of semantic content 23 | const LABELS = { 24 | 'TEXT': 0, 25 | 'CODE': 1, 26 | 'LINK': 2, 27 | 'IMAGE': 3, 28 | 'VIDEO': 4, 29 | 'AUDIO': 5, 30 | 'BUTTON': 6, 31 | 'INPUT': 7, 32 | 'FORM': 8, 33 | 'QUOTE': 9, 34 | 'CUSTOM': 10, 35 | 'ICON': 11, 36 | 'HEADER': 12, 37 | 'SUBMIT': 13, 38 | 'FOOTER': 14, 39 | 'NAV': 15 40 | }; 41 | 42 | const COLORS = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080']; 43 | 44 | function drawRect(canvas, x, y, w, h, text, color) { 45 | const ctx = canvas.getContext('2d'); 46 | 47 | // Fill 48 | ctx.globalAlpha = 0.2; 49 | ctx.fillStyle = color; 50 | ctx.fillRect(x, y, w, h); 51 | ctx.globalAlpha = 1.0; 52 | 53 | // Stroke 54 | ctx.globalAlpha = 0.4; 55 | ctx.strokeStyle = color; 56 | ctx.lineWidth = 2; 57 | ctx.strokeRect(x, y, w, h); 58 | ctx.globalAlpha = 1.0; 59 | 60 | // Text 61 | if (text){ 62 | ctx.font = '12px Arial'; 63 | ctx.fillText(text, x, y); 64 | } 65 | } 66 | 67 | function saveCanvas(canvas, savePath){ 68 | const buffer = canvas.toBuffer('image/png') 69 | fs.writeFileSync(savePath, buffer) 70 | } 71 | 72 | function clipViewport(canvas, viewportHeight, index, save=false, savePath=''){ 73 | let context = canvas.getContext('2d') 74 | let data = context.getImageData(0, viewportHeight * index, canvas.width, viewportHeight) 75 | const canvasViewport = createCanvas(canvas.width, viewportHeight) 76 | const ctx = canvasViewport.getContext('2d') 77 | ctx.putImageData(data, 0, 0) 78 | const buffer = canvasViewport.toBuffer('image/png') 79 | 80 | if (save) saveCanvas(canvasViewport, savePath) 81 | 82 | return buffer 83 | } 84 | 85 | function drawSegments(canvas, segmentGroups, save=false, savePath=''){ 86 | let offset = 0 87 | for (let segments of segmentGroups) { 88 | for (let segment of segments) { 89 | if (segment.label == LABELS.CUSTOM) continue 90 | let bbox = segment.bbox 91 | let description = segment.description 92 | let color = COLORS[segment.label] 93 | let text = Object.keys(LABELS)[segment.label] 94 | drawRect(canvas, bbox.x, bbox.y + offset, bbox.width, bbox.height, text, color) 95 | } 96 | offset += viewportHeight 97 | } 98 | 99 | if (save) saveCanvas(canvas, savePath) 100 | } 101 | 102 | (async () => { 103 | const browser = await chromium.launch({headless: true}) 104 | // const browser = await chromium.launch({headless: false, devtools: true}) 105 | const page = await browser.newPage() 106 | 107 | // sanitize url for a file path by replacing periods and slashes 108 | const imgPathBase = 'screenshots/' + url.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '_').replace(/\./g, '_') + '/' 109 | 110 | await page.setViewportSize({width: viewportWidth, height: viewportHeight}) 111 | await page.goto(url) 112 | 113 | // Seems this was necessary for fonts and stuff, otherwise sizes gets messed up 114 | await page.waitForTimeout(5000) 115 | 116 | // Crawl the webpage, building "leaf node" segments that have semantic content 117 | // Return a list of lists of segments, where each list of segments is a viewport 118 | let segmentGroups = await page.evaluate(([viewportWidth, viewportHeight, LABELS]) => { 119 | 120 | class Segment { 121 | // Leaf node segment of "semantic content" 122 | constructor(el, offset=0){ 123 | this.el = el 124 | this.bbox = this.getBoundingBox(el) 125 | this.label = this.getLabel(el) 126 | this.description = this.getDescription(el) 127 | // this.isClickable = this.isClickable(el) 128 | 129 | if (offset !== 0) 130 | this.bbox.y += offset 131 | } 132 | isClickable(el){ 133 | let hasClickEvent = (el.getAttribute('onclick') != null || el.getAttribute('href') != null) 134 | // let hasClickEvent = (window.getEventListeners && window.getEventListeners(el)['click']) 135 | if (hasClickEvent) return true 136 | else if (el.parentElement) hasClickEvent = this.isClickable(el.parentElement) 137 | return hasClickEvent 138 | } 139 | getBoundingBox(el){ 140 | // return el.getClientRects()[0] 141 | return el.getBoundingClientRect() 142 | } 143 | getLabel(el){ 144 | const data = { 145 | 'tagName': el.tagName.toUpperCase(), 146 | 'parentTagName': el.parentElement.tagName.toUpperCase(), 147 | 'classlist': el.classList, 148 | 'hasText': el.textContent.trim() !== '', 149 | 'el': el 150 | } 151 | 152 | // Classify the semantic content of the node 153 | if (this.isHeader(data)) return LABELS.HEADER 154 | if (this.isCode(data)) return LABELS.CODE 155 | if (this.isQuote(data)) return LABELS.QUOTE 156 | if (this.isList(data)) return LABELS.LIST 157 | if (this.isButton(data)) return LABELS.BUTTON 158 | if (this.isLink(data)) return LABELS.LINK 159 | if (this.isInput(data)) return LABELS.INPUT 160 | if (this.isText(data)) return LABELS.TEXT 161 | if (this.isImage(data)) return LABELS.IMAGE 162 | if (this.isIcon(data)) return LABELS.ICON 163 | if (this.isCustom(data)) return LABELS.CUSTOM 164 | 165 | // Nodes whose labels are their tagname 166 | if (['VIDEO', 'AUDIO', 'FORM', 'NAV', 'FOOTER'].includes(data.tagName)) 167 | return LABELS[data.tagName] 168 | 169 | return data.tagName 170 | } 171 | isButton(data){ 172 | // Should go after a link check 173 | let tagName = data.tagName 174 | let classlist = data.classlist 175 | let role = data.el.getAttribute('role') 176 | let hasClickEvent = (window.getEventListeners && window.getEventListeners(data.el)['click']) 177 | let isInputButton = tagName == 'INPUT' && data.el.getAttribute('type') == 'button' 178 | return tagName == 'BUTTON' || role == 'button' || isInputButton || (tagName == 'A' && (classlist.contains('btn') || classlist.contains('button'))) || hasClickEvent 179 | } 180 | isInput(data){ 181 | let tagName = data.tagName 182 | return tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'OPTION' || tagName == 'TEXTAREA' 183 | } 184 | isCustom(data){ 185 | let tagName = data.tagName 186 | return window.customElements.get(tagName.toLowerCase()) 187 | } 188 | isText(data){ 189 | let tagName = data.tagName 190 | let hasText = data.hasText 191 | return hasText && (tagName == 'P' || tagName == 'SPAN' || tagName == 'ABBR' || tagName == 'LABEL' || tagName == 'DIV'|| tagName == 'LI') 192 | } 193 | isLink(data){ 194 | let tagName = data.tagName 195 | let parentTagName = data.parentTagName 196 | let classlist = data.classlist 197 | 198 | let linkNotButton = (tagName == 'A' && !classlist.contains('btn') && !classlist.contains('button')) 199 | let parentIsLink = (parentTagName == 'A' && (tagName == 'P' || tagName == 'SPAN' || tagName == 'ABBR' || tagName == 'ADDRESS')) 200 | let citeElement = (tagName == 'CITE') 201 | return linkNotButton || parentIsLink || citeElement 202 | } 203 | isHeader(data){ 204 | let tagName = data.tagName 205 | let parentTagName = data.parentTagName 206 | let hasText = data.hasText 207 | 208 | let isHeader = (hasText && (tagName == 'H1' || tagName == 'H2' || tagName == 'H3' || tagName == 'H4' || tagName == 'H5' || tagName == 'H6')) 209 | let isParentHeader = (hasText && (parentTagName == 'H1' || parentTagName == 'H2' || parentTagName == 'H3' || parentTagName == 'H4' || parentTagName == 'H5' || parentTagName == 'H6')) 210 | return isHeader || isParentHeader 211 | } 212 | isCode(data){ 213 | let tagName = data.tagName 214 | return (tagName == 'PRE' || tagName == 'CODE') 215 | } 216 | isQuote(data){ 217 | let tagName = data.tagName 218 | return (tagName == 'BLOCKQUOTE') 219 | } 220 | isImage(data){ 221 | // todo: check if SVG or IMG 222 | let tagName = data.tagName 223 | let el = data.el 224 | if (tagName == 'IMG' || (tagName == 'SVG' && this.bbox.height * this.bbox.width > minImageArea)) 225 | return true 226 | else { 227 | let isBackgroundImage = (window.getComputedStyle(el).backgroundImage.slice(0,3) == 'url') 228 | if (isBackgroundImage){ 229 | let url = window.getComputedStyle(el).backgroundImage.slice(4, -1).replace(/"/g, "") 230 | let filetype = url.split('.').pop() 231 | if (['jpg', 'png', 'gif', 'jpeg', 'webp'].includes(filetype)) 232 | return true 233 | } 234 | return false 235 | } 236 | } 237 | isIcon(data){ 238 | let tagName = data.tagName 239 | let el = data.el 240 | let isSVG = (tagName == 'SVG') 241 | let isSmall = (this.bbox.height * this.bbox.width < minImageArea) 242 | let isKBD = (tagName == 'KBD') 243 | 244 | if (isKBD || isSVG && isSmall){ 245 | return true 246 | } 247 | else { 248 | let isBackgroundImage = (window.getComputedStyle(el).backgroundImage.slice(0,3) == 'url') 249 | if (isBackgroundImage){ 250 | let url = window.getComputedStyle(el).backgroundImage.slice(4, -1).replace(/"/g, "") 251 | let filetype = url.split('.').pop() 252 | if (filetype == 'svg' || filetype.startsWith('data')) 253 | return true 254 | } 255 | else return false 256 | } 257 | } 258 | isList(data){ 259 | let tagName = data.tagName 260 | return (tagName == 'TABLE' || tagName == 'UL' || tagName == 'OL'|| tagName == 'DL') 261 | } 262 | getDescription(el){ 263 | if (el.getAttribute('aria-label')) 264 | return el.getAttribute('aria-label') 265 | if (el.getAttribute('alt')) 266 | return el.getAttribute('alt') 267 | if (el.getAttribute('role')) 268 | return el.getAttribute('role') 269 | return '' 270 | } 271 | getSegmentFromPoint(viewportHeight){ 272 | let centerX = this.bbox.x + this.bbox.width / 2 273 | let centerY = this.bbox.y + this.bbox.height / 2 274 | 275 | // Scroll page so element is in view in increments of `viewportHeight` 276 | let offset = 0 277 | while (this.bbox.y > offset + viewportHeight) 278 | offset += viewportHeight 279 | 280 | let scrollY = window.scrollY 281 | if (scrollY != offset) window.scrollTo(0, offset) 282 | 283 | let el = document.elementFromPoint(centerX, centerY - offset) 284 | 285 | if (el === null) return false 286 | 287 | return new Segment(el, offset) 288 | } 289 | replaceWithSegmentFromPoint(viewportHeight){ 290 | let segment = this.getSegmentFromPoint(viewportHeight) 291 | if (segment === false) return false 292 | 293 | this.el = segment.el 294 | this.bbox = segment.bbox 295 | this.label = segment.label 296 | 297 | return true 298 | } 299 | serialize(){ 300 | return { 301 | 'label': this.label, 302 | 'bbox': { 303 | 'x': this.bbox.x, 304 | 'y': this.bbox.y, 305 | 'width': this.bbox.width, 306 | 'height': this.bbox.height 307 | }, 308 | 'description': this.description 309 | } 310 | } 311 | } 312 | 313 | class Segments { 314 | // Segments is an iterable collection of Segment objects 315 | constructor(segments){ 316 | this.segments = segments || [] 317 | } 318 | add(segment){ 319 | this.segments.push(segment) 320 | return this 321 | } 322 | sort(){ 323 | this.segments.sort((a, b) => a.bbox.y - b.bbox.y) 324 | return this 325 | } 326 | [Symbol.iterator]() { 327 | let index = -1; 328 | let data = this.segments; 329 | 330 | return { 331 | next: () => ({ value: data[++index], done: !(index in data) }) 332 | }; 333 | }; 334 | uniquify(){ 335 | let uniqueSegments = [] 336 | let seen = new Set() 337 | for (let segment of this.segments){ 338 | let key = segment.bbox.x + '_' + segment.bbox.y + '_' + segment.bbox.width + '_' + segment.bbox.height 339 | if (!seen.has(key)){ 340 | uniqueSegments.push(segment) 341 | seen.add(key) 342 | } 343 | } 344 | return new Segments(uniqueSegments) 345 | } 346 | replaceWithSegmentFromPoint(){ 347 | for (let segment of this.segments){ 348 | segment.replaceWithSegmentFromPoint(viewportHeight) 349 | } 350 | return this 351 | } 352 | serialize(){ 353 | let serializedSegments = [] 354 | for (let segment of this.segments){ 355 | serializedSegments.push(segment.serialize()) 356 | } 357 | return serializedSegments 358 | } 359 | } 360 | 361 | class SegmentGroups { 362 | // SegmentGroups is an iterable collection of SegmentGroup objects, broken down by viewport height essentially 363 | constructor(segments){ 364 | this.groups = [] 365 | this.segments = segments || new Segments() 366 | } 367 | group(viewportHeight){ 368 | let group = new Segments() 369 | let offset = 0 370 | 371 | for (let segment of this.segments){ 372 | if (segment.bbox.y < offset + viewportHeight){ 373 | segment.bbox.y -= offset 374 | group.add(segment) 375 | } 376 | else { 377 | this.groups.push(group) 378 | group = new Segments() 379 | offset += viewportHeight 380 | } 381 | } 382 | this.groups.push(group) 383 | return this 384 | } 385 | serialize(){ 386 | let serializedSegmentGroups = [] 387 | for (let group of this.groups){ 388 | serializedSegmentGroups.push(group.serialize()) 389 | } 390 | return serializedSegmentGroups 391 | } 392 | } 393 | 394 | // Types of DOM nodes 395 | const invisible_nodes = ['HEAD', 'META', 'STYLE', 'NOSCRIPT', 'SCRIPT', 'TEMPLATE', 'CENTER', 'DATA', 'EMBED', '', 'BDI'] 396 | const skipped_nodes = ['IFAME', 'BR', 'B', 'I', 'STRONG', 'EM', 'LEGEND'] 397 | const leaf_nodes = ['SVG', 'IMG', 'PRE', 'CODE', 'TEXTAREA', 'INPUT', 'BLOCKQUOTE'] 398 | const leaf_nodes_composite = ['TABLE', 'UL', 'OL', 'DL', 'P', 'BUTTON', 'FORM', 'FOOTER', 'NAV'] 399 | 400 | const minImageArea = 800 401 | 402 | // Breadth-first traversal of DOM tree 403 | let els = [document.body] 404 | let leaves = new Segments() 405 | while (els.length > 0) { 406 | let el = els.shift() 407 | 408 | if (el.nodeType == Node.TEXT_NODE && el.textContent.trim() !== ''){ 409 | leaves.add(new Segment(el.parentElement)) 410 | continue 411 | } 412 | 413 | if (el.nodeType !== Node.ELEMENT_NODE) 414 | continue 415 | 416 | let nodeName = el.nodeName.toUpperCase() 417 | 418 | // Ignore elements that are not visible 419 | if (invisible_nodes.includes(nodeName) || skipped_nodes.includes(nodeName)) 420 | continue 421 | 422 | let computedStyle = window.getComputedStyle(el) 423 | if (computedStyle.visibility == 'hidden' || computedStyle.visibility == 'none' || computedStyle.opacity === '0' || el.checkVisibility() === false) 424 | continue 425 | 426 | // Atomic elements that we want to also double click into 427 | if (leaf_nodes_composite.includes(nodeName)) 428 | leaves.add(new Segment(el)) 429 | 430 | // Atomic elements 431 | if (leaf_nodes.includes(nodeName)){ 432 | leaves.add(new Segment(el)) 433 | continue 434 | } 435 | 436 | // Other leaf elements: background images and text 437 | if (el.childElementCount == 0){ 438 | if (el.textContent.trim() !== '') 439 | leaves.add(new Segment(el)) 440 | 441 | if (window.getComputedStyle(el).backgroundImage.slice(0,3) == 'url') 442 | leaves.add(new Segment(el)) 443 | 444 | continue 445 | } 446 | 447 | els.push(...el.childNodes) 448 | } 449 | 450 | // let uniqueSegments = leaves.uniquify().replaceWithSegmentFromPoint().sort() 451 | let uniqueSegments = leaves.uniquify().sort() 452 | // let uniqueSegments = leaves.sort() 453 | 454 | let segmentGroups = new SegmentGroups(uniqueSegments).group(viewportHeight) 455 | 456 | return segmentGroups.serialize() 457 | }, [viewportWidth, viewportHeight, LABELS]) 458 | 459 | // Generate full-page screenshot 460 | let imgPath = imgPathBase + 'full.png' 461 | await page.evaluate(() => {window.scrollTo(0, 0)}) 462 | await page.screenshot({path: imgPath, fullPage: true}) 463 | let img = await loadImage(imgPath) 464 | 465 | // Create two canvases for screenshot, one for raw and one for annotated 466 | const canvasFull = createCanvas(img.width, img.height) 467 | const ctxFull = canvasFull.getContext('2d') 468 | ctxFull.drawImage(img, 0, 0, img.width, img.height); 469 | 470 | const canvasAnnotated = createCanvas(img.width, img.height) 471 | const ctxAnnotated = canvasAnnotated.getContext('2d') 472 | ctxAnnotated.drawImage(img, 0, 0, img.width, img.height); 473 | 474 | // Draw bounding boxes on annotated canvas 475 | let imgPathFullAnnotated = imgPathBase + 'full_annotated.png' 476 | drawSegments(canvasAnnotated, segmentGroups, save=true, imgPathFullAnnotated) 477 | 478 | // Split up full-page screenshot into segments according to full-page scrolling of viewport 479 | for (let index in segmentGroups){ 480 | let imgPath = imgPathBase + index + '.png' 481 | let imgPathAnnotated = imgPathBase + index + '_annotated.png' 482 | 483 | clipViewport(canvasFull, viewportHeight, index, save=true, savePath=imgPath) 484 | clipViewport(canvasAnnotated, viewportHeight, index, save=true, savePath=imgPathAnnotated) 485 | } 486 | 487 | await browser.close() 488 | })(); -------------------------------------------------------------------------------- /examples/amazon_annotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/amazon_annotated.png -------------------------------------------------------------------------------- /examples/amazon_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/amazon_raw.png -------------------------------------------------------------------------------- /examples/github_annotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/github_annotated.png -------------------------------------------------------------------------------- /examples/github_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/github_raw.png -------------------------------------------------------------------------------- /examples/wikipedia_annotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/wikipedia_annotated.png -------------------------------------------------------------------------------- /examples/wikipedia_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/wikipedia_raw.png -------------------------------------------------------------------------------- /examples/wsj_annotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/wsj_annotated.png -------------------------------------------------------------------------------- /examples/wsj_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmvaldman/html_semantic_seg/b3ece336556ede257b084445cff938388e798419/examples/wsj_raw.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html_semantic_seg", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "canvas": "^2.11.0" 12 | }, 13 | "devDependencies": { 14 | "@playwright/test": "^1.29.0", 15 | "playwright": "^1.29.1", 16 | "playwright-core": "^1.29.0" 17 | } 18 | }, 19 | "node_modules/@mapbox/node-pre-gyp": { 20 | "version": "1.0.10", 21 | "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", 22 | "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", 23 | "dependencies": { 24 | "detect-libc": "^2.0.0", 25 | "https-proxy-agent": "^5.0.0", 26 | "make-dir": "^3.1.0", 27 | "node-fetch": "^2.6.7", 28 | "nopt": "^5.0.0", 29 | "npmlog": "^5.0.1", 30 | "rimraf": "^3.0.2", 31 | "semver": "^7.3.5", 32 | "tar": "^6.1.11" 33 | }, 34 | "bin": { 35 | "node-pre-gyp": "bin/node-pre-gyp" 36 | } 37 | }, 38 | "node_modules/@playwright/test": { 39 | "version": "1.29.0", 40 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.0.tgz", 41 | "integrity": "sha512-gp5PVBenxTJsm2bATWDNc2CCnrL5OaA/MXQdJwwkGQtqTjmY+ZOqAdLqo49O9MLTDh2vYh+tHWDnmFsILnWaeA==", 42 | "dev": true, 43 | "dependencies": { 44 | "@types/node": "*", 45 | "playwright-core": "1.29.0" 46 | }, 47 | "bin": { 48 | "playwright": "cli.js" 49 | }, 50 | "engines": { 51 | "node": ">=14" 52 | } 53 | }, 54 | "node_modules/@types/node": { 55 | "version": "18.11.17", 56 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", 57 | "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", 58 | "dev": true 59 | }, 60 | "node_modules/abbrev": { 61 | "version": "1.1.1", 62 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 63 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 64 | }, 65 | "node_modules/agent-base": { 66 | "version": "6.0.2", 67 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 68 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 69 | "dependencies": { 70 | "debug": "4" 71 | }, 72 | "engines": { 73 | "node": ">= 6.0.0" 74 | } 75 | }, 76 | "node_modules/ansi-regex": { 77 | "version": "5.0.1", 78 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 79 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 80 | "engines": { 81 | "node": ">=8" 82 | } 83 | }, 84 | "node_modules/aproba": { 85 | "version": "2.0.0", 86 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 87 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 88 | }, 89 | "node_modules/are-we-there-yet": { 90 | "version": "2.0.0", 91 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 92 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 93 | "dependencies": { 94 | "delegates": "^1.0.0", 95 | "readable-stream": "^3.6.0" 96 | }, 97 | "engines": { 98 | "node": ">=10" 99 | } 100 | }, 101 | "node_modules/balanced-match": { 102 | "version": "1.0.2", 103 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 104 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 105 | }, 106 | "node_modules/brace-expansion": { 107 | "version": "1.1.11", 108 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 109 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 110 | "dependencies": { 111 | "balanced-match": "^1.0.0", 112 | "concat-map": "0.0.1" 113 | } 114 | }, 115 | "node_modules/canvas": { 116 | "version": "2.11.0", 117 | "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.0.tgz", 118 | "integrity": "sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==", 119 | "hasInstallScript": true, 120 | "dependencies": { 121 | "@mapbox/node-pre-gyp": "^1.0.0", 122 | "nan": "^2.17.0", 123 | "simple-get": "^3.0.3" 124 | }, 125 | "engines": { 126 | "node": ">=6" 127 | } 128 | }, 129 | "node_modules/chownr": { 130 | "version": "2.0.0", 131 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 132 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", 133 | "engines": { 134 | "node": ">=10" 135 | } 136 | }, 137 | "node_modules/color-support": { 138 | "version": "1.1.3", 139 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 140 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", 141 | "bin": { 142 | "color-support": "bin.js" 143 | } 144 | }, 145 | "node_modules/concat-map": { 146 | "version": "0.0.1", 147 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 148 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 149 | }, 150 | "node_modules/console-control-strings": { 151 | "version": "1.1.0", 152 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 153 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 154 | }, 155 | "node_modules/debug": { 156 | "version": "4.3.4", 157 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 158 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 159 | "dependencies": { 160 | "ms": "2.1.2" 161 | }, 162 | "engines": { 163 | "node": ">=6.0" 164 | }, 165 | "peerDependenciesMeta": { 166 | "supports-color": { 167 | "optional": true 168 | } 169 | } 170 | }, 171 | "node_modules/decompress-response": { 172 | "version": "4.2.1", 173 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", 174 | "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", 175 | "dependencies": { 176 | "mimic-response": "^2.0.0" 177 | }, 178 | "engines": { 179 | "node": ">=8" 180 | } 181 | }, 182 | "node_modules/delegates": { 183 | "version": "1.0.0", 184 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 185 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 186 | }, 187 | "node_modules/detect-libc": { 188 | "version": "2.0.1", 189 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", 190 | "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", 191 | "engines": { 192 | "node": ">=8" 193 | } 194 | }, 195 | "node_modules/emoji-regex": { 196 | "version": "8.0.0", 197 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 198 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 199 | }, 200 | "node_modules/fs-minipass": { 201 | "version": "2.1.0", 202 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 203 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 204 | "dependencies": { 205 | "minipass": "^3.0.0" 206 | }, 207 | "engines": { 208 | "node": ">= 8" 209 | } 210 | }, 211 | "node_modules/fs-minipass/node_modules/minipass": { 212 | "version": "3.3.6", 213 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 214 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 215 | "dependencies": { 216 | "yallist": "^4.0.0" 217 | }, 218 | "engines": { 219 | "node": ">=8" 220 | } 221 | }, 222 | "node_modules/fs.realpath": { 223 | "version": "1.0.0", 224 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 225 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 226 | }, 227 | "node_modules/gauge": { 228 | "version": "3.0.2", 229 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 230 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 231 | "dependencies": { 232 | "aproba": "^1.0.3 || ^2.0.0", 233 | "color-support": "^1.1.2", 234 | "console-control-strings": "^1.0.0", 235 | "has-unicode": "^2.0.1", 236 | "object-assign": "^4.1.1", 237 | "signal-exit": "^3.0.0", 238 | "string-width": "^4.2.3", 239 | "strip-ansi": "^6.0.1", 240 | "wide-align": "^1.1.2" 241 | }, 242 | "engines": { 243 | "node": ">=10" 244 | } 245 | }, 246 | "node_modules/glob": { 247 | "version": "7.2.3", 248 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 249 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 250 | "dependencies": { 251 | "fs.realpath": "^1.0.0", 252 | "inflight": "^1.0.4", 253 | "inherits": "2", 254 | "minimatch": "^3.1.1", 255 | "once": "^1.3.0", 256 | "path-is-absolute": "^1.0.0" 257 | }, 258 | "engines": { 259 | "node": "*" 260 | }, 261 | "funding": { 262 | "url": "https://github.com/sponsors/isaacs" 263 | } 264 | }, 265 | "node_modules/has-unicode": { 266 | "version": "2.0.1", 267 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 268 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 269 | }, 270 | "node_modules/https-proxy-agent": { 271 | "version": "5.0.1", 272 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 273 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 274 | "dependencies": { 275 | "agent-base": "6", 276 | "debug": "4" 277 | }, 278 | "engines": { 279 | "node": ">= 6" 280 | } 281 | }, 282 | "node_modules/inflight": { 283 | "version": "1.0.6", 284 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 285 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 286 | "dependencies": { 287 | "once": "^1.3.0", 288 | "wrappy": "1" 289 | } 290 | }, 291 | "node_modules/inherits": { 292 | "version": "2.0.4", 293 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 294 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 295 | }, 296 | "node_modules/is-fullwidth-code-point": { 297 | "version": "3.0.0", 298 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 299 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 300 | "engines": { 301 | "node": ">=8" 302 | } 303 | }, 304 | "node_modules/lru-cache": { 305 | "version": "6.0.0", 306 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 307 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 308 | "dependencies": { 309 | "yallist": "^4.0.0" 310 | }, 311 | "engines": { 312 | "node": ">=10" 313 | } 314 | }, 315 | "node_modules/make-dir": { 316 | "version": "3.1.0", 317 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 318 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 319 | "dependencies": { 320 | "semver": "^6.0.0" 321 | }, 322 | "engines": { 323 | "node": ">=8" 324 | }, 325 | "funding": { 326 | "url": "https://github.com/sponsors/sindresorhus" 327 | } 328 | }, 329 | "node_modules/make-dir/node_modules/semver": { 330 | "version": "6.3.0", 331 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 332 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 333 | "bin": { 334 | "semver": "bin/semver.js" 335 | } 336 | }, 337 | "node_modules/mimic-response": { 338 | "version": "2.1.0", 339 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", 340 | "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", 341 | "engines": { 342 | "node": ">=8" 343 | }, 344 | "funding": { 345 | "url": "https://github.com/sponsors/sindresorhus" 346 | } 347 | }, 348 | "node_modules/minimatch": { 349 | "version": "3.1.2", 350 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 351 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 352 | "dependencies": { 353 | "brace-expansion": "^1.1.7" 354 | }, 355 | "engines": { 356 | "node": "*" 357 | } 358 | }, 359 | "node_modules/minipass": { 360 | "version": "4.0.0", 361 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", 362 | "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", 363 | "dependencies": { 364 | "yallist": "^4.0.0" 365 | }, 366 | "engines": { 367 | "node": ">=8" 368 | } 369 | }, 370 | "node_modules/minizlib": { 371 | "version": "2.1.2", 372 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 373 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 374 | "dependencies": { 375 | "minipass": "^3.0.0", 376 | "yallist": "^4.0.0" 377 | }, 378 | "engines": { 379 | "node": ">= 8" 380 | } 381 | }, 382 | "node_modules/minizlib/node_modules/minipass": { 383 | "version": "3.3.6", 384 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 385 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 386 | "dependencies": { 387 | "yallist": "^4.0.0" 388 | }, 389 | "engines": { 390 | "node": ">=8" 391 | } 392 | }, 393 | "node_modules/mkdirp": { 394 | "version": "1.0.4", 395 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 396 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 397 | "bin": { 398 | "mkdirp": "bin/cmd.js" 399 | }, 400 | "engines": { 401 | "node": ">=10" 402 | } 403 | }, 404 | "node_modules/ms": { 405 | "version": "2.1.2", 406 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 407 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 408 | }, 409 | "node_modules/nan": { 410 | "version": "2.17.0", 411 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", 412 | "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" 413 | }, 414 | "node_modules/node-fetch": { 415 | "version": "2.6.7", 416 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 417 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 418 | "dependencies": { 419 | "whatwg-url": "^5.0.0" 420 | }, 421 | "engines": { 422 | "node": "4.x || >=6.0.0" 423 | }, 424 | "peerDependencies": { 425 | "encoding": "^0.1.0" 426 | }, 427 | "peerDependenciesMeta": { 428 | "encoding": { 429 | "optional": true 430 | } 431 | } 432 | }, 433 | "node_modules/nopt": { 434 | "version": "5.0.0", 435 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 436 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 437 | "dependencies": { 438 | "abbrev": "1" 439 | }, 440 | "bin": { 441 | "nopt": "bin/nopt.js" 442 | }, 443 | "engines": { 444 | "node": ">=6" 445 | } 446 | }, 447 | "node_modules/npmlog": { 448 | "version": "5.0.1", 449 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 450 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 451 | "dependencies": { 452 | "are-we-there-yet": "^2.0.0", 453 | "console-control-strings": "^1.1.0", 454 | "gauge": "^3.0.0", 455 | "set-blocking": "^2.0.0" 456 | } 457 | }, 458 | "node_modules/object-assign": { 459 | "version": "4.1.1", 460 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 461 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 462 | "engines": { 463 | "node": ">=0.10.0" 464 | } 465 | }, 466 | "node_modules/once": { 467 | "version": "1.4.0", 468 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 469 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 470 | "dependencies": { 471 | "wrappy": "1" 472 | } 473 | }, 474 | "node_modules/path-is-absolute": { 475 | "version": "1.0.1", 476 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 477 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 478 | "engines": { 479 | "node": ">=0.10.0" 480 | } 481 | }, 482 | "node_modules/playwright": { 483 | "version": "1.29.1", 484 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.29.1.tgz", 485 | "integrity": "sha512-lasC+pMqsQ2uWhNurt3YK3xo0gWlMjslYUylKbHcqF/NTjwp9KStRGO7S6wwz2f52GcSnop8XUK/GymJjdzrxw==", 486 | "dev": true, 487 | "hasInstallScript": true, 488 | "dependencies": { 489 | "playwright-core": "1.29.1" 490 | }, 491 | "bin": { 492 | "playwright": "cli.js" 493 | }, 494 | "engines": { 495 | "node": ">=14" 496 | } 497 | }, 498 | "node_modules/playwright-core": { 499 | "version": "1.29.0", 500 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.0.tgz", 501 | "integrity": "sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==", 502 | "dev": true, 503 | "bin": { 504 | "playwright": "cli.js" 505 | }, 506 | "engines": { 507 | "node": ">=14" 508 | } 509 | }, 510 | "node_modules/playwright/node_modules/playwright-core": { 511 | "version": "1.29.1", 512 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.1.tgz", 513 | "integrity": "sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg==", 514 | "dev": true, 515 | "bin": { 516 | "playwright": "cli.js" 517 | }, 518 | "engines": { 519 | "node": ">=14" 520 | } 521 | }, 522 | "node_modules/readable-stream": { 523 | "version": "3.6.0", 524 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 525 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 526 | "dependencies": { 527 | "inherits": "^2.0.3", 528 | "string_decoder": "^1.1.1", 529 | "util-deprecate": "^1.0.1" 530 | }, 531 | "engines": { 532 | "node": ">= 6" 533 | } 534 | }, 535 | "node_modules/rimraf": { 536 | "version": "3.0.2", 537 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 538 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 539 | "dependencies": { 540 | "glob": "^7.1.3" 541 | }, 542 | "bin": { 543 | "rimraf": "bin.js" 544 | }, 545 | "funding": { 546 | "url": "https://github.com/sponsors/isaacs" 547 | } 548 | }, 549 | "node_modules/safe-buffer": { 550 | "version": "5.2.1", 551 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 552 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 553 | "funding": [ 554 | { 555 | "type": "github", 556 | "url": "https://github.com/sponsors/feross" 557 | }, 558 | { 559 | "type": "patreon", 560 | "url": "https://www.patreon.com/feross" 561 | }, 562 | { 563 | "type": "consulting", 564 | "url": "https://feross.org/support" 565 | } 566 | ] 567 | }, 568 | "node_modules/semver": { 569 | "version": "7.3.8", 570 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", 571 | "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", 572 | "dependencies": { 573 | "lru-cache": "^6.0.0" 574 | }, 575 | "bin": { 576 | "semver": "bin/semver.js" 577 | }, 578 | "engines": { 579 | "node": ">=10" 580 | } 581 | }, 582 | "node_modules/set-blocking": { 583 | "version": "2.0.0", 584 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 585 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 586 | }, 587 | "node_modules/signal-exit": { 588 | "version": "3.0.7", 589 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 590 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 591 | }, 592 | "node_modules/simple-concat": { 593 | "version": "1.0.1", 594 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 595 | "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 596 | "funding": [ 597 | { 598 | "type": "github", 599 | "url": "https://github.com/sponsors/feross" 600 | }, 601 | { 602 | "type": "patreon", 603 | "url": "https://www.patreon.com/feross" 604 | }, 605 | { 606 | "type": "consulting", 607 | "url": "https://feross.org/support" 608 | } 609 | ] 610 | }, 611 | "node_modules/simple-get": { 612 | "version": "3.1.1", 613 | "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", 614 | "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", 615 | "dependencies": { 616 | "decompress-response": "^4.2.0", 617 | "once": "^1.3.1", 618 | "simple-concat": "^1.0.0" 619 | } 620 | }, 621 | "node_modules/string_decoder": { 622 | "version": "1.3.0", 623 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 624 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 625 | "dependencies": { 626 | "safe-buffer": "~5.2.0" 627 | } 628 | }, 629 | "node_modules/string-width": { 630 | "version": "4.2.3", 631 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 632 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 633 | "dependencies": { 634 | "emoji-regex": "^8.0.0", 635 | "is-fullwidth-code-point": "^3.0.0", 636 | "strip-ansi": "^6.0.1" 637 | }, 638 | "engines": { 639 | "node": ">=8" 640 | } 641 | }, 642 | "node_modules/strip-ansi": { 643 | "version": "6.0.1", 644 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 645 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 646 | "dependencies": { 647 | "ansi-regex": "^5.0.1" 648 | }, 649 | "engines": { 650 | "node": ">=8" 651 | } 652 | }, 653 | "node_modules/tar": { 654 | "version": "6.1.13", 655 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", 656 | "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", 657 | "dependencies": { 658 | "chownr": "^2.0.0", 659 | "fs-minipass": "^2.0.0", 660 | "minipass": "^4.0.0", 661 | "minizlib": "^2.1.1", 662 | "mkdirp": "^1.0.3", 663 | "yallist": "^4.0.0" 664 | }, 665 | "engines": { 666 | "node": ">=10" 667 | } 668 | }, 669 | "node_modules/tr46": { 670 | "version": "0.0.3", 671 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 672 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 673 | }, 674 | "node_modules/util-deprecate": { 675 | "version": "1.0.2", 676 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 677 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 678 | }, 679 | "node_modules/webidl-conversions": { 680 | "version": "3.0.1", 681 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 682 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 683 | }, 684 | "node_modules/whatwg-url": { 685 | "version": "5.0.0", 686 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 687 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 688 | "dependencies": { 689 | "tr46": "~0.0.3", 690 | "webidl-conversions": "^3.0.0" 691 | } 692 | }, 693 | "node_modules/wide-align": { 694 | "version": "1.1.5", 695 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 696 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 697 | "dependencies": { 698 | "string-width": "^1.0.2 || 2 || 3 || 4" 699 | } 700 | }, 701 | "node_modules/wrappy": { 702 | "version": "1.0.2", 703 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 704 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 705 | }, 706 | "node_modules/yallist": { 707 | "version": "4.0.0", 708 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 709 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 710 | } 711 | }, 712 | "dependencies": { 713 | "@mapbox/node-pre-gyp": { 714 | "version": "1.0.10", 715 | "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", 716 | "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", 717 | "requires": { 718 | "detect-libc": "^2.0.0", 719 | "https-proxy-agent": "^5.0.0", 720 | "make-dir": "^3.1.0", 721 | "node-fetch": "^2.6.7", 722 | "nopt": "^5.0.0", 723 | "npmlog": "^5.0.1", 724 | "rimraf": "^3.0.2", 725 | "semver": "^7.3.5", 726 | "tar": "^6.1.11" 727 | } 728 | }, 729 | "@playwright/test": { 730 | "version": "1.29.0", 731 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.0.tgz", 732 | "integrity": "sha512-gp5PVBenxTJsm2bATWDNc2CCnrL5OaA/MXQdJwwkGQtqTjmY+ZOqAdLqo49O9MLTDh2vYh+tHWDnmFsILnWaeA==", 733 | "dev": true, 734 | "requires": { 735 | "@types/node": "*", 736 | "playwright-core": "1.29.0" 737 | } 738 | }, 739 | "@types/node": { 740 | "version": "18.11.17", 741 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", 742 | "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", 743 | "dev": true 744 | }, 745 | "abbrev": { 746 | "version": "1.1.1", 747 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 748 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 749 | }, 750 | "agent-base": { 751 | "version": "6.0.2", 752 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 753 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 754 | "requires": { 755 | "debug": "4" 756 | } 757 | }, 758 | "ansi-regex": { 759 | "version": "5.0.1", 760 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 761 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" 762 | }, 763 | "aproba": { 764 | "version": "2.0.0", 765 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 766 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 767 | }, 768 | "are-we-there-yet": { 769 | "version": "2.0.0", 770 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 771 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 772 | "requires": { 773 | "delegates": "^1.0.0", 774 | "readable-stream": "^3.6.0" 775 | } 776 | }, 777 | "balanced-match": { 778 | "version": "1.0.2", 779 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 780 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 781 | }, 782 | "brace-expansion": { 783 | "version": "1.1.11", 784 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 785 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 786 | "requires": { 787 | "balanced-match": "^1.0.0", 788 | "concat-map": "0.0.1" 789 | } 790 | }, 791 | "canvas": { 792 | "version": "2.11.0", 793 | "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.0.tgz", 794 | "integrity": "sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==", 795 | "requires": { 796 | "@mapbox/node-pre-gyp": "^1.0.0", 797 | "nan": "^2.17.0", 798 | "simple-get": "^3.0.3" 799 | } 800 | }, 801 | "chownr": { 802 | "version": "2.0.0", 803 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 804 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" 805 | }, 806 | "color-support": { 807 | "version": "1.1.3", 808 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 809 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" 810 | }, 811 | "concat-map": { 812 | "version": "0.0.1", 813 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 814 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 815 | }, 816 | "console-control-strings": { 817 | "version": "1.1.0", 818 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 819 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 820 | }, 821 | "debug": { 822 | "version": "4.3.4", 823 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 824 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 825 | "requires": { 826 | "ms": "2.1.2" 827 | } 828 | }, 829 | "decompress-response": { 830 | "version": "4.2.1", 831 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", 832 | "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", 833 | "requires": { 834 | "mimic-response": "^2.0.0" 835 | } 836 | }, 837 | "delegates": { 838 | "version": "1.0.0", 839 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 840 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 841 | }, 842 | "detect-libc": { 843 | "version": "2.0.1", 844 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", 845 | "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" 846 | }, 847 | "emoji-regex": { 848 | "version": "8.0.0", 849 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 850 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 851 | }, 852 | "fs-minipass": { 853 | "version": "2.1.0", 854 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 855 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 856 | "requires": { 857 | "minipass": "^3.0.0" 858 | }, 859 | "dependencies": { 860 | "minipass": { 861 | "version": "3.3.6", 862 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 863 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 864 | "requires": { 865 | "yallist": "^4.0.0" 866 | } 867 | } 868 | } 869 | }, 870 | "fs.realpath": { 871 | "version": "1.0.0", 872 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 873 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 874 | }, 875 | "gauge": { 876 | "version": "3.0.2", 877 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 878 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 879 | "requires": { 880 | "aproba": "^1.0.3 || ^2.0.0", 881 | "color-support": "^1.1.2", 882 | "console-control-strings": "^1.0.0", 883 | "has-unicode": "^2.0.1", 884 | "object-assign": "^4.1.1", 885 | "signal-exit": "^3.0.0", 886 | "string-width": "^4.2.3", 887 | "strip-ansi": "^6.0.1", 888 | "wide-align": "^1.1.2" 889 | } 890 | }, 891 | "glob": { 892 | "version": "7.2.3", 893 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 894 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 895 | "requires": { 896 | "fs.realpath": "^1.0.0", 897 | "inflight": "^1.0.4", 898 | "inherits": "2", 899 | "minimatch": "^3.1.1", 900 | "once": "^1.3.0", 901 | "path-is-absolute": "^1.0.0" 902 | } 903 | }, 904 | "has-unicode": { 905 | "version": "2.0.1", 906 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 907 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 908 | }, 909 | "https-proxy-agent": { 910 | "version": "5.0.1", 911 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 912 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 913 | "requires": { 914 | "agent-base": "6", 915 | "debug": "4" 916 | } 917 | }, 918 | "inflight": { 919 | "version": "1.0.6", 920 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 921 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 922 | "requires": { 923 | "once": "^1.3.0", 924 | "wrappy": "1" 925 | } 926 | }, 927 | "inherits": { 928 | "version": "2.0.4", 929 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 930 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 931 | }, 932 | "is-fullwidth-code-point": { 933 | "version": "3.0.0", 934 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 935 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 936 | }, 937 | "lru-cache": { 938 | "version": "6.0.0", 939 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 940 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 941 | "requires": { 942 | "yallist": "^4.0.0" 943 | } 944 | }, 945 | "make-dir": { 946 | "version": "3.1.0", 947 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 948 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 949 | "requires": { 950 | "semver": "^6.0.0" 951 | }, 952 | "dependencies": { 953 | "semver": { 954 | "version": "6.3.0", 955 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 956 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 957 | } 958 | } 959 | }, 960 | "mimic-response": { 961 | "version": "2.1.0", 962 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", 963 | "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" 964 | }, 965 | "minimatch": { 966 | "version": "3.1.2", 967 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 968 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 969 | "requires": { 970 | "brace-expansion": "^1.1.7" 971 | } 972 | }, 973 | "minipass": { 974 | "version": "4.0.0", 975 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", 976 | "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", 977 | "requires": { 978 | "yallist": "^4.0.0" 979 | } 980 | }, 981 | "minizlib": { 982 | "version": "2.1.2", 983 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 984 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 985 | "requires": { 986 | "minipass": "^3.0.0", 987 | "yallist": "^4.0.0" 988 | }, 989 | "dependencies": { 990 | "minipass": { 991 | "version": "3.3.6", 992 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 993 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 994 | "requires": { 995 | "yallist": "^4.0.0" 996 | } 997 | } 998 | } 999 | }, 1000 | "mkdirp": { 1001 | "version": "1.0.4", 1002 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1003 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" 1004 | }, 1005 | "ms": { 1006 | "version": "2.1.2", 1007 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1008 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1009 | }, 1010 | "nan": { 1011 | "version": "2.17.0", 1012 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", 1013 | "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" 1014 | }, 1015 | "node-fetch": { 1016 | "version": "2.6.7", 1017 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 1018 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 1019 | "requires": { 1020 | "whatwg-url": "^5.0.0" 1021 | } 1022 | }, 1023 | "nopt": { 1024 | "version": "5.0.0", 1025 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 1026 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 1027 | "requires": { 1028 | "abbrev": "1" 1029 | } 1030 | }, 1031 | "npmlog": { 1032 | "version": "5.0.1", 1033 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 1034 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 1035 | "requires": { 1036 | "are-we-there-yet": "^2.0.0", 1037 | "console-control-strings": "^1.1.0", 1038 | "gauge": "^3.0.0", 1039 | "set-blocking": "^2.0.0" 1040 | } 1041 | }, 1042 | "object-assign": { 1043 | "version": "4.1.1", 1044 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1045 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1046 | }, 1047 | "once": { 1048 | "version": "1.4.0", 1049 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1050 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1051 | "requires": { 1052 | "wrappy": "1" 1053 | } 1054 | }, 1055 | "path-is-absolute": { 1056 | "version": "1.0.1", 1057 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1058 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 1059 | }, 1060 | "playwright": { 1061 | "version": "1.29.1", 1062 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.29.1.tgz", 1063 | "integrity": "sha512-lasC+pMqsQ2uWhNurt3YK3xo0gWlMjslYUylKbHcqF/NTjwp9KStRGO7S6wwz2f52GcSnop8XUK/GymJjdzrxw==", 1064 | "dev": true, 1065 | "requires": { 1066 | "playwright-core": "1.29.1" 1067 | }, 1068 | "dependencies": { 1069 | "playwright-core": { 1070 | "version": "1.29.1", 1071 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.1.tgz", 1072 | "integrity": "sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg==", 1073 | "dev": true 1074 | } 1075 | } 1076 | }, 1077 | "playwright-core": { 1078 | "version": "1.29.0", 1079 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.0.tgz", 1080 | "integrity": "sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==", 1081 | "dev": true 1082 | }, 1083 | "readable-stream": { 1084 | "version": "3.6.0", 1085 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1086 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1087 | "requires": { 1088 | "inherits": "^2.0.3", 1089 | "string_decoder": "^1.1.1", 1090 | "util-deprecate": "^1.0.1" 1091 | } 1092 | }, 1093 | "rimraf": { 1094 | "version": "3.0.2", 1095 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1096 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1097 | "requires": { 1098 | "glob": "^7.1.3" 1099 | } 1100 | }, 1101 | "safe-buffer": { 1102 | "version": "5.2.1", 1103 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1104 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1105 | }, 1106 | "semver": { 1107 | "version": "7.3.8", 1108 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", 1109 | "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", 1110 | "requires": { 1111 | "lru-cache": "^6.0.0" 1112 | } 1113 | }, 1114 | "set-blocking": { 1115 | "version": "2.0.0", 1116 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1117 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 1118 | }, 1119 | "signal-exit": { 1120 | "version": "3.0.7", 1121 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1122 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 1123 | }, 1124 | "simple-concat": { 1125 | "version": "1.0.1", 1126 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 1127 | "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" 1128 | }, 1129 | "simple-get": { 1130 | "version": "3.1.1", 1131 | "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", 1132 | "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", 1133 | "requires": { 1134 | "decompress-response": "^4.2.0", 1135 | "once": "^1.3.1", 1136 | "simple-concat": "^1.0.0" 1137 | } 1138 | }, 1139 | "string_decoder": { 1140 | "version": "1.3.0", 1141 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1142 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1143 | "requires": { 1144 | "safe-buffer": "~5.2.0" 1145 | } 1146 | }, 1147 | "string-width": { 1148 | "version": "4.2.3", 1149 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1150 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1151 | "requires": { 1152 | "emoji-regex": "^8.0.0", 1153 | "is-fullwidth-code-point": "^3.0.0", 1154 | "strip-ansi": "^6.0.1" 1155 | } 1156 | }, 1157 | "strip-ansi": { 1158 | "version": "6.0.1", 1159 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1160 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1161 | "requires": { 1162 | "ansi-regex": "^5.0.1" 1163 | } 1164 | }, 1165 | "tar": { 1166 | "version": "6.1.13", 1167 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", 1168 | "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", 1169 | "requires": { 1170 | "chownr": "^2.0.0", 1171 | "fs-minipass": "^2.0.0", 1172 | "minipass": "^4.0.0", 1173 | "minizlib": "^2.1.1", 1174 | "mkdirp": "^1.0.3", 1175 | "yallist": "^4.0.0" 1176 | } 1177 | }, 1178 | "tr46": { 1179 | "version": "0.0.3", 1180 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1181 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1182 | }, 1183 | "util-deprecate": { 1184 | "version": "1.0.2", 1185 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1186 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1187 | }, 1188 | "webidl-conversions": { 1189 | "version": "3.0.1", 1190 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1191 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1192 | }, 1193 | "whatwg-url": { 1194 | "version": "5.0.0", 1195 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1196 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1197 | "requires": { 1198 | "tr46": "~0.0.3", 1199 | "webidl-conversions": "^3.0.0" 1200 | } 1201 | }, 1202 | "wide-align": { 1203 | "version": "1.1.5", 1204 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 1205 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 1206 | "requires": { 1207 | "string-width": "^1.0.2 || 2 || 3 || 4" 1208 | } 1209 | }, 1210 | "wrappy": { 1211 | "version": "1.0.2", 1212 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1213 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1214 | }, 1215 | "yallist": { 1216 | "version": "4.0.0", 1217 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1218 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1219 | } 1220 | } 1221 | } 1222 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html_semantic_seg", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "canvas": "^2.11.0", 12 | "@playwright/test": "^1.29.0", 13 | "playwright": "^1.29.1", 14 | "playwright-core": "^1.29.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { devices } = require('@playwright/test'); 3 | 4 | /** 5 | * Read environment variables from file. 6 | * https://github.com/motdotla/dotenv 7 | */ 8 | // require('dotenv').config(); 9 | 10 | 11 | /** 12 | * @see https://playwright.dev/docs/test-configuration 13 | * @type {import('@playwright/test').PlaywrightTestConfig} 14 | */ 15 | const config = { 16 | testDir: './tests', 17 | /* Maximum time one test can run for. */ 18 | timeout: 30 * 1000, 19 | expect: { 20 | /** 21 | * Maximum time expect() should wait for the condition to be met. 22 | * For example in `await expect(locator).toHaveText();` 23 | */ 24 | timeout: 5000 25 | }, 26 | /* Run tests in files in parallel */ 27 | fullyParallel: true, 28 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 29 | forbidOnly: !!process.env.CI, 30 | /* Retry on CI only */ 31 | retries: process.env.CI ? 2 : 0, 32 | /* Opt out of parallel tests on CI. */ 33 | workers: process.env.CI ? 1 : undefined, 34 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 35 | reporter: 'html', 36 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 37 | use: { 38 | /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ 39 | actionTimeout: 0, 40 | /* Base URL to use in actions like `await page.goto('/')`. */ 41 | // baseURL: 'http://localhost:3000', 42 | 43 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 44 | trace: 'on-first-retry', 45 | }, 46 | 47 | /* Configure projects for major browsers */ 48 | projects: [ 49 | { 50 | name: 'chromium', 51 | use: { 52 | ...devices['Desktop Chrome'], 53 | }, 54 | }, 55 | 56 | { 57 | name: 'firefox', 58 | use: { 59 | ...devices['Desktop Firefox'], 60 | }, 61 | }, 62 | 63 | { 64 | name: 'webkit', 65 | use: { 66 | ...devices['Desktop Safari'], 67 | }, 68 | }, 69 | 70 | /* Test against mobile viewports. */ 71 | // { 72 | // name: 'Mobile Chrome', 73 | // use: { 74 | // ...devices['Pixel 5'], 75 | // }, 76 | // }, 77 | // { 78 | // name: 'Mobile Safari', 79 | // use: { 80 | // ...devices['iPhone 12'], 81 | // }, 82 | // }, 83 | 84 | /* Test against branded browsers. */ 85 | // { 86 | // name: 'Microsoft Edge', 87 | // use: { 88 | // channel: 'msedge', 89 | // }, 90 | // }, 91 | // { 92 | // name: 'Google Chrome', 93 | // use: { 94 | // channel: 'chrome', 95 | // }, 96 | // }, 97 | ], 98 | 99 | /* Folder for test artifacts such as screenshots, videos, traces, etc. */ 100 | // outputDir: 'test-results/', 101 | 102 | /* Run your local dev server before starting the tests */ 103 | // webServer: { 104 | // command: 'npm run start', 105 | // port: 3000, 106 | // }, 107 | }; 108 | 109 | module.exports = config; 110 | --------------------------------------------------------------------------------