├── .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 |
--------------------------------------------------------------------------------