From 6ed502c6189dc454ce9308d493ea044972c21e05 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Fri, 25 Aug 2023 14:22:49 +0300 Subject: [PATCH 01/21] ci(e2e): e2e with playwright --- .github/workflows/node.js.yml | 13 +- packages/js/.gitignore | 4 + packages/js/package-lock.json | 61 +++- packages/js/package.json | 2 + packages/js/playwright.config.ts | 77 +++++ packages/js/src/browser.ts | 7 +- packages/js/test/e2e/404.html | 1 + packages/js/test/e2e/integration.spec.ts | 385 +++++++++++++++++++++++ packages/js/test/e2e/sandbox.html | 43 +++ packages/js/test/e2e/server.js | 48 +++ 10 files changed, 624 insertions(+), 17 deletions(-) create mode 100644 packages/js/playwright.config.ts create mode 100644 packages/js/test/e2e/404.html create mode 100644 packages/js/test/e2e/integration.spec.ts create mode 100644 packages/js/test/e2e/sandbox.html create mode 100644 packages/js/test/e2e/server.js diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 1c1bf0e88..578cb9850 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -65,10 +65,8 @@ jobs: - 'packages/js/**' integration: - # integration tests from "js" package fail if they are executed at the same time needs: changes if: needs.changes.outputs.core == 'true' - concurrency: js-concurrency runs-on: ubuntu-latest steps: - name: Checkout @@ -81,22 +79,17 @@ jobs: cache: 'npm' cache-dependency-path: | package-lock.json - packages/js/test/integration/package-lock.json + packages/js/package-lock.json - name: Build - run: | - npm ci - cd packages/js/test/integration - npm ci + run: npm ci - name: Run integration tests env: CI: true - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} run: | cd packages/js - npm run test:integration + npm run test:integration:playwright lint: diff --git a/packages/js/.gitignore b/packages/js/.gitignore index 90d95862c..88a466cdb 100644 --- a/packages/js/.gitignore +++ b/packages/js/.gitignore @@ -1 +1,5 @@ local.log +node_modules/ +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/packages/js/package-lock.json b/packages/js/package-lock.json index 2739d1a5e..90c04022d 100644 --- a/packages/js/package-lock.json +++ b/packages/js/package-lock.json @@ -14,6 +14,7 @@ "@types/express": "^4.17.13" }, "devDependencies": { + "@playwright/test": "^1.37.1", "@rollup/plugin-commonjs": "^22.0.1", "@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-replace": "^5.0.2", @@ -633,9 +634,9 @@ "dev": true }, "node_modules/@honeybadger-io/core": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.1.0.tgz", - "integrity": "sha512-t8GonzOfy0zSUvFvFIsvyCrYEyhNi341CDGiNbCQa6XBIISCr4BZPnXmnfRwqQZsVY6H5xyip4aZ8iT8LvvdXA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.2.0.tgz", + "integrity": "sha512-3j3b0uDs5ZW/8r22dozLLAVKSwMoyGD/YzMRRJ/cOTJD9ch3duJ2FrfJRD1VaSZ9nK26c7NBYU9Nlra4apEw5g==", "dependencies": { "stacktrace-parser": "^0.1.10" }, @@ -1050,6 +1051,25 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz", + "integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.37.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -4959,6 +4979,18 @@ "node": ">=8" } }, + "node_modules/playwright-core": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", + "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/plur": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", @@ -6971,9 +7003,9 @@ "dev": true }, "@honeybadger-io/core": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.1.0.tgz", - "integrity": "sha512-t8GonzOfy0zSUvFvFIsvyCrYEyhNi341CDGiNbCQa6XBIISCr4BZPnXmnfRwqQZsVY6H5xyip4aZ8iT8LvvdXA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.2.0.tgz", + "integrity": "sha512-3j3b0uDs5ZW/8r22dozLLAVKSwMoyGD/YzMRRJ/cOTJD9ch3duJ2FrfJRD1VaSZ9nK26c7NBYU9Nlra4apEw5g==", "requires": { "stacktrace-parser": "^0.1.10" } @@ -7305,6 +7337,17 @@ "fastq": "^1.6.0" } }, + "@playwright/test": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz", + "integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==", + "dev": true, + "requires": { + "@types/node": "*", + "fsevents": "2.3.2", + "playwright-core": "1.37.1" + } + }, "@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -10348,6 +10391,12 @@ "find-up": "^4.0.0" } }, + "playwright-core": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", + "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", + "dev": true + }, "plur": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", diff --git a/packages/js/package.json b/packages/js/package.json index 66da30b6d..2f749b1ec 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -44,6 +44,7 @@ "test:browser": "jest --env=jsdom --setupFiles=\"/test/unit/jest-browser-setup.js\" --testPathPattern=\"test/unit/.*(? => { + await page.goto('/') + await expect(page).toHaveTitle('Integration Sandbox') + + const hbClient = await page.evaluate('window.Honeybadger') + expect(hbClient).toBeDefined() + expect(hbClient.config.apiKey).toEqual('integration_sandbox') +} + +const triggerException = async (page: Page) => { + await page.click('button#throw-exception') +} + +test.beforeEach(async ({ page }, _testInfo) => { + await setup(page) +}) + +test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { + await triggerException(page) + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') +}) + +test('it notifies Honeybadger manually', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('expected message')) + await expect(resultHandle.jsonValue()).resolves.toEqual(true) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('expected message') +}) + +test('it reports multiple errors in the same process', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 1')) + await expect(resultHandle.jsonValue()).resolves.toEqual(true) + await resultHandle.dispose() + + const resultHandle2 = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 2')) + await expect(resultHandle2.jsonValue()).resolves.toEqual(true) + await resultHandle2.dispose() + + await triggerException(page) + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(3) +}) + +test('it sends console breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + console.log('expected message') + window.Honeybadger.notify('testing') + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('expected message'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('log'); +}) + +test('it sends string value console breadcrumbs when null', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + console.log(null) + window.Honeybadger.notify('testing') + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('null') + expect(notices[0].breadcrumbs.trail[0].category).toEqual('log') +}) + +test('it sends click breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + const button = document.getElementById('normal-button') + button.click() + window.Honeybadger.notify('testing') + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('button#normal-button') + expect(notices[0].breadcrumbs.trail[0].category).toEqual('ui.click') + expect(notices[0].breadcrumbs.trail[0].metadata.selector).toEqual('body > div#buttonDivId > button#normal-button') + expect(notices[0].breadcrumbs.trail[0].metadata.text).toEqual('normal button') +}) + +test.skip('it sends XHR breadcrumbs for relative paths', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + return new Promise(function (resolve, _reject) { + const request = new XMLHttpRequest() + request.open('GET', '/example/path', false); + request.onreadystatechange = function () { + if (request.readyState === 4) { + window.Honeybadger.notify('testing') + resolve() + } + }; + request.send(null) + }) + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1); + expect(notices[0].breadcrumbs.trail.length).toEqual(2); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); + expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); + expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); +}) + +test.skip('it sends XHR breadcrumbs for absolute paths', async ({ page }) => { + +}) + +test.skip('it sends fetch breadcrumbs', async ({ page }) => { + +}) + +test.skip('it sends navigation breadcrumbs', async ({ page }) => { + +}) + +test('it sends notify breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + window.Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(1); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('notice'); + expect(notices[0].breadcrumbs.trail[0].metadata).toMatchObject({ + name: 'expected name', + message: 'expected message', + stack: 'expected stack' + }); + expect(notices[0].breadcrumbs.trail[0].metadata).not.toHaveProperty('context'); +}) + +test('it sends window.onerror breadcrumbs', async ({ page }) => { + await triggerException(page) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) + expect(errorBreadcrumbs.length).toEqual(1) + expect(errorBreadcrumbs[0].message).toMatch('Error'); + expect(errorBreadcrumbs[0].category).toEqual('error'); + expect(errorBreadcrumbs[0].metadata).toMatchObject({ + message: 'unhandled exception with known stack trace', + name: 'Error', + }); + const errorStack = await page.evaluate('results.error.stack') + expect(errorBreadcrumbs[0].metadata.stack).toMatch(errorStack) +}) + +test('it skips onunhandledrejection when already sent', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const supportsUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + + const resultHandle2 = await page.evaluateHandle(async _ => { + const promise = new Promise(function (_res, _rej) { + throw new Error('unhandled exception') + }); + return promise.catch(function (err) { window.Honeybadger.notify(err) }) + }) + await resultHandle2.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) +}) + +test('it sends window.onunhandledrejection breadcrumbs when rejection is an Error', async ({ page }) => { + // let errorStack = null + // page.on('pageerror', (err) => errorStack = err.stack) + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const supportsUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + + const handle = await page.evaluateHandle(_ => { + const myPromise = new Promise(() => { + throw new Error('unhandled rejection') + }) + myPromise.then(() => { }) + return Promise.resolve() + }) + await handle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) + expect(errorBreadcrumbs.length).toEqual(1) + expect(errorBreadcrumbs[0].message).toEqual('window.onunhandledrejection: Error') + expect(errorBreadcrumbs[0].category).toEqual('error') + expect(errorBreadcrumbs[0].metadata).toMatchObject({ + message: 'UnhandledPromiseRejectionWarning: Error: unhandled rejection', + name: 'Error', + }) + + // fixme: only works in chromium + // expect(errorBreadcrumbs[0].metadata.stack).toMatch(errorStack) +}) + +test('it skips window.onunhandledrejection breadcrumbs when rejection is not Error', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const supportsUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + + const handle = await page.evaluateHandle(_ => { + const myPromise = new Promise((_, reject) => { + reject('whatever') + }) + myPromise.then(() => { }) + return Promise.resolve() + }) + await handle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) + expect(errorBreadcrumbs.length).toEqual(0) +}) + +test('it shows user feedback form', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { + // @ts-expect-error private access + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-expect-error private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') + }) + await handle.dispose() + + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') + + const formHeading = page.locator('h2#honeybadger-feedback-heading') + await expect(formHeading.textContent()).resolves.toMatch('Care to help us fix this?') +}) + +test('it shows user feedback form with custom labels', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { + // @ts-expect-error private access + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-expect-error private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm({ + messages: { + heading: 'Help us fix this', + } + }) + }) + window.Honeybadger.notify('an error message') + }) + await handle.dispose() + + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') + + const formHeading = page.locator('h2#honeybadger-feedback-heading') + await expect(formHeading.textContent()).resolves.toMatch('Help us fix this') +}) + +test('it sends user feedback for notice on submit', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { + // @ts-expect-error private access + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-expect-error private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') + }) + await handle.dispose() + + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') + + const name = 'integration test' + const email = 'integration-test@honeybadger.io' + const comment = 'ci integration comment' + await page.type('#honeybadger-feedback-name', name) + await page.type('#honeybadger-feedback-email', email) + await page.type('#honeybadger-feedback-comment', comment) + await page.click('#honeybadger-feedback-submit') + + const feedbackSubmitUrl = 'https://api.honeybadger.io/v2/feedback' + + '?format=js' + + '&api_key=integration_sandbox' + + '&token=test' + + `&name=${encodeURIComponent(name)}` + + `&email=${encodeURIComponent(email)}` + + `&comment=${encodeURIComponent(comment)}` + + const form = page.locator('form#honeybadger-feedback-form') + const htmlString = await form.innerHTML() + expect(htmlString).toContain(``) +}) + +test('it closes user feedback form on cancel', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { + // @ts-expect-error private access + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-expect-error private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') + }) + await handle.dispose() + + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') + + expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(1); + await page.click('#honeybadger-feedback-cancel') + expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(0); +}) diff --git a/packages/js/test/e2e/sandbox.html b/packages/js/test/e2e/sandbox.html new file mode 100644 index 000000000..30e93c336 --- /dev/null +++ b/packages/js/test/e2e/sandbox.html @@ -0,0 +1,43 @@ + + + + + + + Integration Sandbox + + + +
+ +
+ anchor text + + + + + + diff --git a/packages/js/test/e2e/server.js b/packages/js/test/e2e/server.js new file mode 100644 index 000000000..ee2dd762b --- /dev/null +++ b/packages/js/test/e2e/server.js @@ -0,0 +1,48 @@ +const fs = require('fs') +const http = require('http') +const path = require('path') + +const PORT = process.env.PORT || 3000; + +const MIME_TYPES = { + default: 'application/octet-stream', + html: 'text/html; charset=UTF-8', + js: 'application/javascript', + css: 'text/css', + png: 'image/png', + jpg: 'image/jpg', + gif: 'image/gif', + ico: 'image/x-icon', + svg: 'image/svg+xml', +}; + +const STATIC_PATH = path.resolve(__dirname, '../../') + +const toBool = [() => true, () => false]; + +const prepareFile = async (url) => { + const paths = [STATIC_PATH, url]; + if (url.endsWith('/')) paths.push('test/e2e/sandbox.html'); + const filePath = path.join(...paths); + console.log('looking for', filePath) + const pathTraversal = !filePath.startsWith(STATIC_PATH); + const exists = await fs.promises.access(filePath).then(...toBool); + const found = !pathTraversal && exists; + const streamPath = found ? filePath : STATIC_PATH + '/test/e2e/404.html'; + const ext = path.extname(streamPath).substring(1).toLowerCase(); + const stream = fs.createReadStream(streamPath); + return { found, ext, stream }; +}; + +http + .createServer(async (req, res) => { + const file = await prepareFile(req.url) + const statusCode = file.found ? 200 : 404; + const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default; + res.writeHead(statusCode, { 'Content-Type': mimeType }); + file.stream.pipe(res); + console.log(`${req.method} ${req.url} ${statusCode}`); + }) + .listen(PORT); + +console.log(`Server running at http://127.0.0.1:${PORT}/`); From c14661a73d4ee65d92451101b1cb983d0e4724eb Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Fri, 25 Aug 2023 14:35:24 +0300 Subject: [PATCH 02/21] chore: fix ci --- .github/workflows/node.js.yml | 3 ++ packages/js/test/e2e/integration.spec.ts | 58 ++++++++++++------------ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 578cb9850..d4dd9b68d 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -84,6 +84,9 @@ jobs: - name: Build run: npm ci + - name: Instal Playwright Browsers + run: npx playwright install --with-deps + - name: Run integration tests env: CI: true diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index e67f7da03..b80bf2c33 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -1,8 +1,10 @@ import { test, expect, Page } from '@playwright/test' import { resolve } from 'path' import type Honeybadger from '../../honeybadger' +import type { NoticeTransportPayload } from '../../../core/src/types' -declare const window: { Honeybadger: Honeybadger, results: { notices: unknown[], error: Error } } +type Results = { notices: NoticeTransportPayload[], error: Error } +declare const window: { Honeybadger: Honeybadger, results: Results } const setup = async (page: Page): Promise => { await page.goto('/') @@ -24,7 +26,7 @@ test.beforeEach(async ({ page }, _testInfo) => { test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { await triggerException(page) - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') }) @@ -34,7 +36,7 @@ test('it notifies Honeybadger manually', async ({ page }) => { await expect(resultHandle.jsonValue()).resolves.toEqual(true) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].error.message).toEqual('expected message') }) @@ -50,7 +52,7 @@ test('it reports multiple errors in the same process', async ({ page }) => { await triggerException(page) - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(3) }) @@ -61,7 +63,7 @@ test('it sends console breadcrumbs', async ({ page }) => { }) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].breadcrumbs.trail.length).toEqual(2) expect(notices[0].breadcrumbs.trail[0].message).toEqual('expected message'); @@ -75,7 +77,7 @@ test('it sends string value console breadcrumbs when null', async ({ page }) => }) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].breadcrumbs.trail.length).toEqual(2) expect(notices[0].breadcrumbs.trail[0].message).toEqual('null') @@ -90,7 +92,7 @@ test('it sends click breadcrumbs', async ({ page }) => { }) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].breadcrumbs.trail.length).toEqual(2) expect(notices[0].breadcrumbs.trail[0].message).toEqual('button#normal-button') @@ -115,7 +117,7 @@ test.skip('it sends XHR breadcrumbs for relative paths', async ({ page }) => { }) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1); expect(notices[0].breadcrumbs.trail.length).toEqual(2); expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); @@ -124,15 +126,15 @@ test.skip('it sends XHR breadcrumbs for relative paths', async ({ page }) => { expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); }) -test.skip('it sends XHR breadcrumbs for absolute paths', async ({ page }) => { +test.skip('it sends XHR breadcrumbs for absolute paths', async ({ page: _ }) => { }) -test.skip('it sends fetch breadcrumbs', async ({ page }) => { +test.skip('it sends fetch breadcrumbs', async ({ page: _ }) => { }) -test.skip('it sends navigation breadcrumbs', async ({ page }) => { +test.skip('it sends navigation breadcrumbs', async ({ page: _ }) => { }) @@ -142,7 +144,7 @@ test('it sends notify breadcrumbs', async ({ page }) => { }) await resultHandle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].breadcrumbs.trail.length).toEqual(1); expect(notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); @@ -157,7 +159,7 @@ test('it sends notify breadcrumbs', async ({ page }) => { test('it sends window.onerror breadcrumbs', async ({ page }) => { await triggerException(page) - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) @@ -187,7 +189,7 @@ test('it skips onunhandledrejection when already sent', async ({ page }) => { }) await resultHandle2.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) }) @@ -207,7 +209,7 @@ test('it sends window.onunhandledrejection breadcrumbs when rejection is an Erro }) await handle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) @@ -238,7 +240,7 @@ test('it skips window.onunhandledrejection breadcrumbs when rejection is not Err }) await handle.dispose() - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) @@ -248,9 +250,9 @@ test('it skips window.onunhandledrejection breadcrumbs when rejection is not Err test('it shows user feedback form', async ({ page }) => { const handle = await page.evaluateHandle(_ => { - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.appendUserFeedbackScriptTag = () => { } window.Honeybadger.afterNotify(() => { window.Honeybadger.showUserFeedbackForm() @@ -265,7 +267,7 @@ test('it shows user feedback form', async ({ page }) => { }) await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') @@ -277,9 +279,9 @@ test('it shows user feedback form', async ({ page }) => { test('it shows user feedback form with custom labels', async ({ page }) => { const handle = await page.evaluateHandle(_ => { - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.appendUserFeedbackScriptTag = () => { } window.Honeybadger.afterNotify(() => { window.Honeybadger.showUserFeedbackForm({ @@ -298,7 +300,7 @@ test('it shows user feedback form with custom labels', async ({ page }) => { }) await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') @@ -310,9 +312,9 @@ test('it shows user feedback form with custom labels', async ({ page }) => { test('it sends user feedback for notice on submit', async ({ page }) => { const handle = await page.evaluateHandle(_ => { - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.appendUserFeedbackScriptTag = () => { } window.Honeybadger.afterNotify(() => { window.Honeybadger.showUserFeedbackForm() @@ -327,7 +329,7 @@ test('it sends user feedback for notice on submit', async ({ page }) => { }) await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') @@ -356,9 +358,9 @@ test('it sends user feedback for notice on submit', async ({ page }) => { test('it closes user feedback form on cancel', async ({ page }) => { const handle = await page.evaluateHandle(_ => { - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false - // @ts-expect-error private access + // @ts-ignore private access window.Honeybadger.appendUserFeedbackScriptTag = () => { } window.Honeybadger.afterNotify(() => { window.Honeybadger.showUserFeedbackForm() @@ -373,7 +375,7 @@ test('it closes user feedback form on cancel', async ({ page }) => { }) await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') + const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1) const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') From 717755a11d3742b553efc57e61259636eb559069 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Sat, 26 Aug 2023 16:51:21 +0300 Subject: [PATCH 03/21] chore: migrate all karma tests to playwright --- packages/js/README.md | 13 + packages/js/playwright.config.ts | 8 +- packages/js/test/e2e/integration.spec.ts | 659 +++++++++++++---------- packages/js/test/e2e/server.js | 7 +- packages/js/test/e2e/worker.js | 20 + 5 files changed, 415 insertions(+), 292 deletions(-) create mode 100644 packages/js/test/e2e/worker.js diff --git a/packages/js/README.md b/packages/js/README.md index 9960c17d1..8c6b7cc03 100644 --- a/packages/js/README.md +++ b/packages/js/README.md @@ -29,6 +29,19 @@ However, since the package is isomorphic, TypeScript users will likely be writin account and use `BROWSERSTACK_USERNAME=your_username BROWSERSTACK_ACCESS_KEY=your-access-key npm run test:integration`. 3. To test the TypeScript type definitions: `npm run tsd`. +#### End-to-end tests with Playwright +We use [Playwright](https://playwright.dev) to run integration tests in a real browser. +The config file is at the root of this package: `playwright.config.ts`. +To run these tests locally, you'll need to install the browsers you want to test with. +The easiest way to do this is to use the `npx playwright install --with-deps` command. +Then run `npm run test:integration:playwright`. + +##### Architecture +Inside `./test/e2e`, you will find a `server.js` file that runs a simple nodejs http server. +This server is used to serve the test page, along with other static assets and to receive the error reports from the browser. +The test page is found in `./test/e2e/sandbox.html`. +All tests are found in `./test/e2e/integration.spec.ts`. + ## Releasing This package comes with a `postpublish` script (`scripts/release-cdn.sh`) diff --git a/packages/js/playwright.config.ts b/packages/js/playwright.config.ts index 9d4870afe..c8bc389af 100644 --- a/packages/js/playwright.config.ts +++ b/packages/js/playwright.config.ts @@ -58,10 +58,10 @@ export default defineConfig({ // }, /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, + { + name: 'Microsoft Edge', + use: { ...devices['Desktop Edge'], channel: 'msedge' }, + }, // { // name: 'Google Chrome', // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index b80bf2c33..c3f6ad145 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -4,7 +4,7 @@ import type Honeybadger from '../../honeybadger' import type { NoticeTransportPayload } from '../../../core/src/types' type Results = { notices: NoticeTransportPayload[], error: Error } -declare const window: { Honeybadger: Honeybadger, results: Results } +declare const window: { Honeybadger: Honeybadger, results: Results, history: History } const setup = async (page: Page): Promise => { await page.goto('/') @@ -19,331 +19,388 @@ const triggerException = async (page: Page) => { await page.click('button#throw-exception') } -test.beforeEach(async ({ page }, _testInfo) => { - await setup(page) +test.beforeAll(async ({ page }) => { + const context = page.context() + const browser = context.browser() + console.log('Running on', browser.browserType(), browser.version()) }) -test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { - await triggerException(page) +test.describe('Browser Integration', () => { + test.beforeEach(async ({ page }, _testInfo) => { + await setup(page) + }) - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') -}) + test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { + await triggerException(page) -test('it notifies Honeybadger manually', async ({ page }) => { - const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('expected message')) - await expect(resultHandle.jsonValue()).resolves.toEqual(true) - await resultHandle.dispose() + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') + }) - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].error.message).toEqual('expected message') -}) + test('it notifies Honeybadger manually', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('expected message')) + await expect(resultHandle.jsonValue()).resolves.toEqual(true) + await resultHandle.dispose() -test('it reports multiple errors in the same process', async ({ page }) => { - const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 1')) - await expect(resultHandle.jsonValue()).resolves.toEqual(true) - await resultHandle.dispose() + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('expected message') + }) - const resultHandle2 = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 2')) - await expect(resultHandle2.jsonValue()).resolves.toEqual(true) - await resultHandle2.dispose() + test('it reports multiple errors in the same process', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 1')) + await expect(resultHandle.jsonValue()).resolves.toEqual(true) + await resultHandle.dispose() - await triggerException(page) + const resultHandle2 = await page.evaluateHandle(_ => window.Honeybadger.notify('notify 2')) + await expect(resultHandle2.jsonValue()).resolves.toEqual(true) + await resultHandle2.dispose() - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(3) -}) + await triggerException(page) -test('it sends console breadcrumbs', async ({ page }) => { - const resultHandle = await page.evaluateHandle(async _ => { - console.log('expected message') - window.Honeybadger.notify('testing') + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(3) }) - await resultHandle.dispose() - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].breadcrumbs.trail.length).toEqual(2) - expect(notices[0].breadcrumbs.trail[0].message).toEqual('expected message'); - expect(notices[0].breadcrumbs.trail[0].category).toEqual('log'); -}) + test('it sends console breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + console.log('expected message') + window.Honeybadger.notify('testing') + }) + await resultHandle.dispose() -test('it sends string value console breadcrumbs when null', async ({ page }) => { - const resultHandle = await page.evaluateHandle(async _ => { - console.log(null) - window.Honeybadger.notify('testing') + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('expected message'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('log'); }) - await resultHandle.dispose() - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].breadcrumbs.trail.length).toEqual(2) - expect(notices[0].breadcrumbs.trail[0].message).toEqual('null') - expect(notices[0].breadcrumbs.trail[0].category).toEqual('log') -}) + test('it sends string value console breadcrumbs when null', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + console.log(null) + window.Honeybadger.notify('testing') + }) + await resultHandle.dispose() -test('it sends click breadcrumbs', async ({ page }) => { - const resultHandle = await page.evaluateHandle(async _ => { - const button = document.getElementById('normal-button') - button.click() - window.Honeybadger.notify('testing') + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('null') + expect(notices[0].breadcrumbs.trail[0].category).toEqual('log') }) - await resultHandle.dispose() - - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].breadcrumbs.trail.length).toEqual(2) - expect(notices[0].breadcrumbs.trail[0].message).toEqual('button#normal-button') - expect(notices[0].breadcrumbs.trail[0].category).toEqual('ui.click') - expect(notices[0].breadcrumbs.trail[0].metadata.selector).toEqual('body > div#buttonDivId > button#normal-button') - expect(notices[0].breadcrumbs.trail[0].metadata.text).toEqual('normal button') -}) -test.skip('it sends XHR breadcrumbs for relative paths', async ({ page }) => { - const resultHandle = await page.evaluateHandle(async _ => { - return new Promise(function (resolve, _reject) { - const request = new XMLHttpRequest() - request.open('GET', '/example/path', false); - request.onreadystatechange = function () { - if (request.readyState === 4) { - window.Honeybadger.notify('testing') - resolve() - } - }; - request.send(null) + test('it sends click breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + const button = document.getElementById('normal-button') + button.click() + window.Honeybadger.notify('testing') }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(2) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('button#normal-button') + expect(notices[0].breadcrumbs.trail[0].category).toEqual('ui.click') + expect(notices[0].breadcrumbs.trail[0].metadata.selector).toEqual('body > div#buttonDivId > button#normal-button') + expect(notices[0].breadcrumbs.trail[0].metadata.text).toEqual('normal button') }) - await resultHandle.dispose() - - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1); - expect(notices[0].breadcrumbs.trail.length).toEqual(2); - expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); - expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); - expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); - expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); -}) - -test.skip('it sends XHR breadcrumbs for absolute paths', async ({ page: _ }) => { -}) + test('it sends XHR breadcrumbs for relative paths', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + return new Promise(function (resolve, _reject) { + const request = new XMLHttpRequest() + request.open('GET', '/example/path', false); + request.onreadystatechange = function () { + if (request.readyState === 4) { + window.Honeybadger.notify('testing') + resolve() + } + }; + request.send(null) + }) + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1); + expect(notices[0].breadcrumbs.trail.length).toEqual(2); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); + expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); + expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); + }) -test.skip('it sends fetch breadcrumbs', async ({ page: _ }) => { + test('it sends XHR breadcrumbs for absolute paths', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + return new Promise(function (resolve, _reject) { + const request = new XMLHttpRequest() + request.withCredentials = true + request.open('GET', 'http://localhost:3000/example/path', false); + request.onreadystatechange = function () { + if (request.readyState === 4) { + window.Honeybadger.notify('testing') + resolve() + } + }; + request.send(null) + }) + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1); + expect(notices[0].breadcrumbs.trail.length).toEqual(2); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET http://localhost:3000/example/path'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); + expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); + expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); + }) -}) + test('it sends fetch breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + return fetch('/example/path') + .then(function () { + window.Honeybadger.notify('testing') + }) + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1); + expect(notices[0].breadcrumbs.trail.length).toEqual(2); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); + expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('fetch'); + expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); + }) -test.skip('it sends navigation breadcrumbs', async ({ page: _ }) => { + test('it sends navigation breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => { + window.history.pushState({}, '', 'foo.html'); + window.Honeybadger.notify('testing'); + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1); + expect(notices[0].breadcrumbs.trail.length).toEqual(2); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('Page changed'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('navigation'); + expect(notices[0].breadcrumbs.trail[0].metadata).toEqual({ + from: 'http://localhost:3000/', + to: 'foo.html' + }); + }) -}) + test('it sends notify breadcrumbs', async ({ page }) => { + const resultHandle = await page.evaluateHandle(async _ => { + window.Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); + }) + await resultHandle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(1); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); + expect(notices[0].breadcrumbs.trail[0].category).toEqual('notice'); + expect(notices[0].breadcrumbs.trail[0].metadata).toMatchObject({ + name: 'expected name', + message: 'expected message', + stack: 'expected stack' + }); + expect(notices[0].breadcrumbs.trail[0].metadata).not.toHaveProperty('context'); + }) -test('it sends notify breadcrumbs', async ({ page }) => { - const resultHandle = await page.evaluateHandle(async _ => { - window.Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); + test('it sends window.onerror breadcrumbs', async ({ page }) => { + await triggerException(page) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) + expect(errorBreadcrumbs.length).toEqual(1) + expect(errorBreadcrumbs[0].message).toMatch('Error'); + expect(errorBreadcrumbs[0].category).toEqual('error'); + expect(errorBreadcrumbs[0].metadata).toMatchObject({ + message: 'unhandled exception with known stack trace', + name: 'Error', + }); + const errorStack = await page.evaluate('results.error.stack') + expect(errorBreadcrumbs[0].metadata.stack).toMatch(errorStack) }) - await resultHandle.dispose() - - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].breadcrumbs.trail.length).toEqual(1); - expect(notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); - expect(notices[0].breadcrumbs.trail[0].category).toEqual('notice'); - expect(notices[0].breadcrumbs.trail[0].metadata).toMatchObject({ - name: 'expected name', - message: 'expected message', - stack: 'expected stack' - }); - expect(notices[0].breadcrumbs.trail[0].metadata).not.toHaveProperty('context'); -}) -test('it sends window.onerror breadcrumbs', async ({ page }) => { - await triggerException(page) - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(notices[0].error.message).toEqual('unhandled exception with known stack trace') - expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) - - const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) - expect(errorBreadcrumbs.length).toEqual(1) - expect(errorBreadcrumbs[0].message).toMatch('Error'); - expect(errorBreadcrumbs[0].category).toEqual('error'); - expect(errorBreadcrumbs[0].metadata).toMatchObject({ - message: 'unhandled exception with known stack trace', - name: 'Error', - }); - const errorStack = await page.evaluate('results.error.stack') - expect(errorBreadcrumbs[0].metadata.stack).toMatch(errorStack) -}) + test('it skips onunhandledrejection when already sent', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const doesNotSupportUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(doesNotSupportUnhandledRejectionListener, 'onunhandledrejection not supported') -test('it skips onunhandledrejection when already sent', async ({ page }) => { - const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) - const supportsUnhandledRejectionListener = await resultHandle.jsonValue() - test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + const resultHandle2 = await page.evaluateHandle(async _ => { + const promise = new Promise(function (_res, _rej) { + throw new Error('unhandled exception') + }); + return promise.catch(function (err) { window.Honeybadger.notify(err) }) + }) + await resultHandle2.dispose() - const resultHandle2 = await page.evaluateHandle(async _ => { - const promise = new Promise(function (_res, _rej) { - throw new Error('unhandled exception') - }); - return promise.catch(function (err) { window.Honeybadger.notify(err) }) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) }) - await resultHandle2.dispose() - - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) -}) -test('it sends window.onunhandledrejection breadcrumbs when rejection is an Error', async ({ page }) => { + test('it sends window.onunhandledrejection breadcrumbs when rejection is an Error', async ({ page }) => { // let errorStack = null // page.on('pageerror', (err) => errorStack = err.stack) - const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) - const supportsUnhandledRejectionListener = await resultHandle.jsonValue() - test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const doesNotSupportUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(doesNotSupportUnhandledRejectionListener, 'onunhandledrejection not supported') - const handle = await page.evaluateHandle(_ => { - const myPromise = new Promise(() => { - throw new Error('unhandled rejection') + const handle = await page.evaluateHandle(_ => { + const myPromise = new Promise(() => { + throw new Error('unhandled rejection') + }) + myPromise.then(() => { }) + return Promise.resolve() + }) + await handle.dispose() + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) + expect(errorBreadcrumbs.length).toEqual(1) + expect(errorBreadcrumbs[0].message).toEqual('window.onunhandledrejection: Error') + expect(errorBreadcrumbs[0].category).toEqual('error') + expect(errorBreadcrumbs[0].metadata).toMatchObject({ + message: 'UnhandledPromiseRejectionWarning: Error: unhandled rejection', + name: 'Error', }) - myPromise.then(() => { }) - return Promise.resolve() - }) - await handle.dispose() - - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) - - const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) - expect(errorBreadcrumbs.length).toEqual(1) - expect(errorBreadcrumbs[0].message).toEqual('window.onunhandledrejection: Error') - expect(errorBreadcrumbs[0].category).toEqual('error') - expect(errorBreadcrumbs[0].metadata).toMatchObject({ - message: 'UnhandledPromiseRejectionWarning: Error: unhandled rejection', - name: 'Error', - }) // fixme: only works in chromium // expect(errorBreadcrumbs[0].metadata.stack).toMatch(errorStack) -}) + }) -test('it skips window.onunhandledrejection breadcrumbs when rejection is not Error', async ({ page }) => { - const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) - const supportsUnhandledRejectionListener = await resultHandle.jsonValue() - test.skip(supportsUnhandledRejectionListener, 'onunhandledrejection not supported') + test('it skips window.onunhandledrejection breadcrumbs when rejection is not Error', async ({ page }) => { + const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) + const doesNotSupportUnhandledRejectionListener = await resultHandle.jsonValue() + test.skip(doesNotSupportUnhandledRejectionListener, 'onunhandledrejection not supported') - const handle = await page.evaluateHandle(_ => { - const myPromise = new Promise((_, reject) => { - reject('whatever') + const handle = await page.evaluateHandle(_ => { + const myPromise = new Promise((_, reject) => { + reject('whatever') + }) + myPromise.then(() => { }) + return Promise.resolve() }) - myPromise.then(() => { }) - return Promise.resolve() - }) - await handle.dispose() + await handle.dispose() - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) - expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) + expect(Array.isArray(notices[0].breadcrumbs.trail)).toEqual(true) - const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) - expect(errorBreadcrumbs.length).toEqual(0) -}) + const errorBreadcrumbs = notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error' }) + expect(errorBreadcrumbs.length).toEqual(0) + }) -test('it shows user feedback form', async ({ page }) => { - const handle = await page.evaluateHandle(_ => { - // @ts-ignore private access - window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + test('it shows user feedback form', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { // @ts-ignore private access - window.Honeybadger.appendUserFeedbackScriptTag = () => { } - window.Honeybadger.afterNotify(() => { - window.Honeybadger.showUserFeedbackForm() + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-ignore private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') }) - window.Honeybadger.notify('an error message') - }) - await handle.dispose() + await handle.dispose() - const relativePath = '../../dist/browser/honeybadger-feedback-form.js' - await page.addScriptTag({ - path: resolve(__dirname, relativePath) - }) - await page.waitForSelector('div#honeybadger-feedback') + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) - const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') - expect(noticeId).toEqual('test') + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') - const formHeading = page.locator('h2#honeybadger-feedback-heading') - await expect(formHeading.textContent()).resolves.toMatch('Care to help us fix this?') -}) + const formHeading = page.locator('h2#honeybadger-feedback-heading') + await expect(formHeading.textContent()).resolves.toMatch('Care to help us fix this?') + }) -test('it shows user feedback form with custom labels', async ({ page }) => { - const handle = await page.evaluateHandle(_ => { - // @ts-ignore private access - window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + test('it shows user feedback form with custom labels', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { // @ts-ignore private access - window.Honeybadger.appendUserFeedbackScriptTag = () => { } - window.Honeybadger.afterNotify(() => { - window.Honeybadger.showUserFeedbackForm({ - messages: { - heading: 'Help us fix this', - } + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-ignore private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm({ + messages: { + heading: 'Help us fix this', + } + }) }) + window.Honeybadger.notify('an error message') }) - window.Honeybadger.notify('an error message') - }) - await handle.dispose() + await handle.dispose() - const relativePath = '../../dist/browser/honeybadger-feedback-form.js' - await page.addScriptTag({ - path: resolve(__dirname, relativePath) - }) - await page.waitForSelector('div#honeybadger-feedback') + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) - const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') - expect(noticeId).toEqual('test') + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') - const formHeading = page.locator('h2#honeybadger-feedback-heading') - await expect(formHeading.textContent()).resolves.toMatch('Help us fix this') -}) + const formHeading = page.locator('h2#honeybadger-feedback-heading') + await expect(formHeading.textContent()).resolves.toMatch('Help us fix this') + }) -test('it sends user feedback for notice on submit', async ({ page }) => { - const handle = await page.evaluateHandle(_ => { + test('it sends user feedback for notice on submit', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { // @ts-ignore private access - window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false - // @ts-ignore private access - window.Honeybadger.appendUserFeedbackScriptTag = () => { } - window.Honeybadger.afterNotify(() => { - window.Honeybadger.showUserFeedbackForm() + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-ignore private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') }) - window.Honeybadger.notify('an error message') - }) - await handle.dispose() + await handle.dispose() - const relativePath = '../../dist/browser/honeybadger-feedback-form.js' - await page.addScriptTag({ - path: resolve(__dirname, relativePath) - }) - await page.waitForSelector('div#honeybadger-feedback') + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) - const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') - expect(noticeId).toEqual('test') + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') - const name = 'integration test' - const email = 'integration-test@honeybadger.io' - const comment = 'ci integration comment' - await page.type('#honeybadger-feedback-name', name) - await page.type('#honeybadger-feedback-email', email) - await page.type('#honeybadger-feedback-comment', comment) - await page.click('#honeybadger-feedback-submit') + const name = 'integration test' + const email = 'integration-test@honeybadger.io' + const comment = 'ci integration comment' + await page.type('#honeybadger-feedback-name', name) + await page.type('#honeybadger-feedback-email', email) + await page.type('#honeybadger-feedback-comment', comment) + await page.click('#honeybadger-feedback-submit') - const feedbackSubmitUrl = 'https://api.honeybadger.io/v2/feedback' + + const feedbackSubmitUrl = 'https://api.honeybadger.io/v2/feedback' + '?format=js' + '&api_key=integration_sandbox' + '&token=test' + @@ -351,37 +408,69 @@ test('it sends user feedback for notice on submit', async ({ page }) => { `&email=${encodeURIComponent(email)}` + `&comment=${encodeURIComponent(comment)}` - const form = page.locator('form#honeybadger-feedback-form') - const htmlString = await form.innerHTML() - expect(htmlString).toContain(``) -}) + const form = page.locator('form#honeybadger-feedback-form') + const htmlString = await form.innerHTML() + expect(htmlString).toContain(``) + }) -test('it closes user feedback form on cancel', async ({ page }) => { - const handle = await page.evaluateHandle(_ => { - // @ts-ignore private access - window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + test('it closes user feedback form on cancel', async ({ page }) => { + const handle = await page.evaluateHandle(_ => { // @ts-ignore private access - window.Honeybadger.appendUserFeedbackScriptTag = () => { } - window.Honeybadger.afterNotify(() => { - window.Honeybadger.showUserFeedbackForm() + window.Honeybadger.isUserFeedbackScriptUrlAlreadyVisible = () => false + // @ts-ignore private access + window.Honeybadger.appendUserFeedbackScriptTag = () => { } + window.Honeybadger.afterNotify(() => { + window.Honeybadger.showUserFeedbackForm() + }) + window.Honeybadger.notify('an error message') }) - window.Honeybadger.notify('an error message') - }) - await handle.dispose() + await handle.dispose() - const relativePath = '../../dist/browser/honeybadger-feedback-form.js' - await page.addScriptTag({ - path: resolve(__dirname, relativePath) - }) - await page.waitForSelector('div#honeybadger-feedback') + const relativePath = '../../dist/browser/honeybadger-feedback-form.js' + await page.addScriptTag({ + path: resolve(__dirname, relativePath) + }) + await page.waitForSelector('div#honeybadger-feedback') + + const { notices } = await page.evaluate('results') + expect(notices.length).toEqual(1) - const { notices } = await page.evaluate('results') - expect(notices.length).toEqual(1) + const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') + expect(noticeId).toEqual('test') + + expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(1); + await page.click('#honeybadger-feedback-cancel') + expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(0); + }) +}) - const noticeId = await page.evaluate('window.honeybadgerUserFeedbackOptions.noticeId') - expect(noticeId).toEqual('test') +test.describe('Web Worker Integration', () => { + test('it creates worker', async ({ page }, _testInfo) => { + const resultHandle = await page.evaluateHandle(_ => !('Worker' in window)) + const doesNotSupportWorkers = await resultHandle.jsonValue() + test.skip(doesNotSupportWorkers, 'Workers are not supported') + + await page.goto('/') + await expect(page).toHaveTitle('Integration Sandbox') + + const { notices }: Results = await page.evaluate(async () => { + return new Promise(resolve => { + // create worker with a uri that will be loaded from the local http server (server.js) + // send a message which will trigger the worker to send the notices back to the test + const worker = new Worker('test/e2e/worker.js') + worker.onmessage = (e) => resolve(e.data) + worker.postMessage('') + }) + }) - expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(1); - await page.click('#honeybadger-feedback-cancel') - expect(await page.locator('#honeybadger-feedback-wrapper').count()).toEqual(0); + expect(notices.length).toEqual(1) + expect(notices[0].breadcrumbs.trail.length).toEqual(1) + expect(notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice') + expect(notices[0].breadcrumbs.trail[0].category).toEqual('notice') + expect(notices[0].breadcrumbs.trail[0].metadata).toMatchObject({ + name: 'expected name', + message: 'expected message', + }); + expect(notices[0].breadcrumbs.trail[0].metadata).not.toHaveProperty('context'); + }) }) diff --git a/packages/js/test/e2e/server.js b/packages/js/test/e2e/server.js index ee2dd762b..8ddbb3042 100644 --- a/packages/js/test/e2e/server.js +++ b/packages/js/test/e2e/server.js @@ -22,9 +22,10 @@ const toBool = [() => true, () => false]; const prepareFile = async (url) => { const paths = [STATIC_PATH, url]; - if (url.endsWith('/')) paths.push('test/e2e/sandbox.html'); - const filePath = path.join(...paths); - console.log('looking for', filePath) + if (url === '/') paths.push('test/e2e/sandbox.html') + + const filePath = path.join(...paths) + console.log('original url', url, '=> looking for', filePath) const pathTraversal = !filePath.startsWith(STATIC_PATH); const exists = await fs.promises.access(filePath).then(...toBool); const found = !pathTraversal && exists; diff --git a/packages/js/test/e2e/worker.js b/packages/js/test/e2e/worker.js new file mode 100644 index 000000000..16844f005 --- /dev/null +++ b/packages/js/test/e2e/worker.js @@ -0,0 +1,20 @@ +self.importScripts('/dist/browser/honeybadger.js') + +self.Honeybadger.configure({ + apiKey: 'integration_sandbox' +}); + + +const results = { notices: [] } + +// This is not a great test since it circumvents fetch...but I can't seem to come up with something better. +self.Honeybadger.__transport.send = function (options, payload) { + results.notices.push(payload); + return Promise.resolve({ statusCode: 201, body: JSON.stringify({ id: 'test' }) }) +}; + +self.Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); + +onmessage = () => { + postMessage(results) +} From 55edddc1f8835ddaee6f2a176b8e43bb0c389908 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Sat, 26 Aug 2023 17:23:16 +0300 Subject: [PATCH 04/21] chore: remove before all --- packages/js/test/e2e/integration.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index c3f6ad145..da310cb85 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -19,17 +19,17 @@ const triggerException = async (page: Page) => { await page.click('button#throw-exception') } -test.beforeAll(async ({ page }) => { - const context = page.context() - const browser = context.browser() - console.log('Running on', browser.browserType(), browser.version()) -}) - test.describe('Browser Integration', () => { test.beforeEach(async ({ page }, _testInfo) => { await setup(page) }) + test('it logs browser type and version', async ({ page }) => { + const context = page.context() + const browser = context.browser() + console.log('Running on', browser.browserType(), browser.version()) + }) + test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { await triggerException(page) From 817c4873152c6633e06354f06ad44d8e8957c238 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Sat, 26 Aug 2023 17:59:03 +0300 Subject: [PATCH 05/21] chore: fix integration test --- packages/js/test/e2e/integration.spec.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index da310cb85..4b213d350 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -27,7 +27,7 @@ test.describe('Browser Integration', () => { test('it logs browser type and version', async ({ page }) => { const context = page.context() const browser = context.browser() - console.log('Running on', browser.browserType(), browser.version()) + console.log('Running on', browser.browserType().name(), browser.version()) }) test('it notifies Honeybadger of unhandled exceptions', async ({ page }) => { @@ -137,8 +137,7 @@ test.describe('Browser Integration', () => { const resultHandle = await page.evaluateHandle(async _ => { return new Promise(function (resolve, _reject) { const request = new XMLHttpRequest() - request.withCredentials = true - request.open('GET', 'http://localhost:3000/example/path', false); + request.open('GET', 'https://example.com/example/path', true); request.onreadystatechange = function () { if (request.readyState === 4) { window.Honeybadger.notify('testing') @@ -153,7 +152,7 @@ test.describe('Browser Integration', () => { const { notices } = await page.evaluate('results') expect(notices.length).toEqual(1); expect(notices[0].breadcrumbs.trail.length).toEqual(2); - expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET http://localhost:3000/example/path'); + expect(notices[0].breadcrumbs.trail[0].message).toEqual('GET https://example.com/example/path'); expect(notices[0].breadcrumbs.trail[0].category).toEqual('request'); expect(notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); expect('message' in notices[0].breadcrumbs.trail[0].metadata).toBe(false); From bce391d4e1c4c877531132d223a5408272849223 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Sun, 27 Aug 2023 15:48:07 +0300 Subject: [PATCH 06/21] feat: playwright and browserstack --- .github/workflows/node.js.yml | 4 +- packages/js/README.md | 21 +- packages/js/package-lock.json | 296 ++++++++++++++---- packages/js/package.json | 6 +- packages/js/test/e2e/browsers.ts | 107 +++++++ packages/js/test/e2e/browserstack.config.ts | 51 +++ packages/js/test/e2e/global-setup.ts | 29 ++ packages/js/test/e2e/global-teardown.ts | 19 ++ packages/js/test/e2e/integration.spec.ts | 29 +- .../js/{ => test/e2e}/playwright.config.ts | 53 +--- 10 files changed, 502 insertions(+), 113 deletions(-) create mode 100644 packages/js/test/e2e/browsers.ts create mode 100644 packages/js/test/e2e/browserstack.config.ts create mode 100644 packages/js/test/e2e/global-setup.ts create mode 100644 packages/js/test/e2e/global-teardown.ts rename packages/js/{ => test/e2e}/playwright.config.ts (51%) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index d4dd9b68d..e88d67b19 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -84,7 +84,7 @@ jobs: - name: Build run: npm ci - - name: Instal Playwright Browsers + - name: Instal; Playwright Browsers run: npx playwright install --with-deps - name: Run integration tests @@ -92,7 +92,7 @@ jobs: CI: true run: | cd packages/js - npm run test:integration:playwright + npm run test:integration lint: diff --git a/packages/js/README.md b/packages/js/README.md index 8c6b7cc03..69be20bb6 100644 --- a/packages/js/README.md +++ b/packages/js/README.md @@ -25,22 +25,31 @@ However, since the package is isomorphic, TypeScript users will likely be writin ### Tests 1. To run unit tests for both browser and server builds: `npm test`. Or separately: `npm run test:browser`, `npm run test:server`. -2. To run integration tests across all supported platforms, set up a [BrowserStack](https://www.browserstack.com/) - account and use `BROWSERSTACK_USERNAME=your_username BROWSERSTACK_ACCESS_KEY=your-access-key npm run test:integration`. +2. To run integration tests across all supported platforms, see [End-to-end tests with Playwright and Browserstack](#end-to-end-tests-with-playwright-and-browserstack-optional). 3. To test the TypeScript type definitions: `npm run tsd`. -#### End-to-end tests with Playwright +#### End-to-end tests with Playwright and Browserstack (optional) We use [Playwright](https://playwright.dev) to run integration tests in a real browser. -The config file is at the root of this package: `playwright.config.ts`. +The config file is at `test/e2e/playwright.config.ts`. To run these tests locally, you'll need to install the browsers you want to test with. -The easiest way to do this is to use the `npx playwright install --with-deps` command. -Then run `npm run test:integration:playwright`. +Open `test/e2e/browsers.ts` and enable the browsers you want to test with. +Then, run `npx playwright install --with-deps` to install the browsers. +Lastly, run `npm run test:integration`. +Additionally, if you want to run the tests on Browserstack: +- enable Browserstack browsers in `browsers.ts`, +- set up a [BrowserStack](https://www.browserstack.com/) account and +- use `BROWSERSTACK_USERNAME=your_username BROWSERSTACK_ACCESS_KEY=your-access-key npm run test:integration`. ##### Architecture Inside `./test/e2e`, you will find a `server.js` file that runs a simple nodejs http server. This server is used to serve the test page, along with other static assets and to receive the error reports from the browser. +The server is automatically started and stopped by Playwright, as you can at the bottom of the `./test/e2e/playwright.config.ts` file. The test page is found in `./test/e2e/sandbox.html`. All tests are found in `./test/e2e/integration.spec.ts`. +Two more configuration files, `./test/e2e/global-setup.ts` and `./test/e2e/global-teardown.ts` are used to start and stop +a local browserstack executable, needed to run the tests on Browserstack. This executable will only be executed if you are testing on Browserstack. +Finally, the `./test/e2e/browserstack.config.ts` file contains the configuration and helper functions to run the tests on Browserstack. + ## Releasing diff --git a/packages/js/package-lock.json b/packages/js/package-lock.json index 90c04022d..218627cc9 100644 --- a/packages/js/package-lock.json +++ b/packages/js/package-lock.json @@ -9,7 +9,6 @@ "version": "6.4.1", "license": "MIT", "dependencies": { - "@honeybadger-io/core": "^6.2.0", "@types/aws-lambda": "^8.10.89", "@types/express": "^4.17.13" }, @@ -20,6 +19,7 @@ "@rollup/plugin-replace": "^5.0.2", "@types/jest": "^27.0.3", "@types/node": "^17.0.45", + "browserstack-local": "^1.5.4", "express": "^4.17.1", "jest": "^27.4.4", "jest-fetch-mock": "^3.0.3", @@ -633,17 +633,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@honeybadger-io/core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.2.0.tgz", - "integrity": "sha512-3j3b0uDs5ZW/8r22dozLLAVKSwMoyGD/YzMRRJ/cOTJD9ch3duJ2FrfJRD1VaSZ9nK26c7NBYU9Nlra4apEw5g==", - "dependencies": { - "stacktrace-parser": "^0.1.10" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1876,6 +1865,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/browserstack-local": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.4.tgz", + "integrity": "sha512-OueHCaQQutO+Fezg+ZTieRn+gdV+JocLjiAQ8nYecu08GhIt3ms79cDHfpoZmECAdoQ6OLdm7ODd+DtQzl4lrA==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "https-proxy-agent": "^5.0.1", + "is-running": "^2.1.0", + "ps-tree": "=1.2.0", + "temp-fs": "^0.9.9" + } + }, "node_modules/bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", @@ -2390,6 +2392,12 @@ "node": ">=8" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2557,6 +2565,21 @@ "node": ">= 0.6" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2782,6 +2805,12 @@ "node": ">= 0.6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3341,6 +3370,12 @@ "@types/estree": "*" } }, + "node_modules/is-running": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", + "integrity": "sha512-mjJd3PujZMl7j+D395WTIO5tU5RIDBfVSRtRR4VOJou3H66E38UjbjvDGh3slJzPuolsb+yQFqwHNNdyp5jg3w==", + "dev": true + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4374,6 +4409,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4940,6 +4981,15 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5073,6 +5123,21 @@ "node": ">= 0.10" } }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -5688,6 +5753,18 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5706,25 +5783,6 @@ "node": ">=10" } }, - "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "engines": { - "node": ">=8" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -5734,6 +5792,15 @@ "node": ">= 0.8" } }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5974,6 +6041,30 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/temp-fs": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", + "integrity": "sha512-WfecDCR1xC9b0nsrzSaxPf3ZuWeWLUWblW4vlDQAa1biQaKHiImHnJfeQocQe/hXKMcolRzgkcVX/7kK4zoWbw==", + "dev": true, + "dependencies": { + "rimraf": "~2.5.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/temp-fs/node_modules/rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha512-Lw7SHMjssciQb/rRz7JyPIy9+bbUshEucPoLRvWqy09vC5zQixl8Uet+Zl+SROBB/JMWHJRdCk1qdxNWHNMvlQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.5" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -6028,6 +6119,12 @@ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -7002,14 +7099,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@honeybadger-io/core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.2.0.tgz", - "integrity": "sha512-3j3b0uDs5ZW/8r22dozLLAVKSwMoyGD/YzMRRJ/cOTJD9ch3duJ2FrfJRD1VaSZ9nK26c7NBYU9Nlra4apEw5g==", - "requires": { - "stacktrace-parser": "^0.1.10" - } - }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -8013,6 +8102,19 @@ "update-browserslist-db": "^1.0.11" } }, + "browserstack-local": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.4.tgz", + "integrity": "sha512-OueHCaQQutO+Fezg+ZTieRn+gdV+JocLjiAQ8nYecu08GhIt3ms79cDHfpoZmECAdoQ6OLdm7ODd+DtQzl4lrA==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "https-proxy-agent": "^5.0.1", + "is-running": "^2.1.0", + "ps-tree": "=1.2.0", + "temp-fs": "^0.9.9" + } + }, "bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", @@ -8403,6 +8505,12 @@ } } }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8524,6 +8632,21 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8710,6 +8833,12 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -9117,6 +9246,12 @@ "@types/estree": "*" } }, + "is-running": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", + "integrity": "sha512-mjJd3PujZMl7j+D395WTIO5tU5RIDBfVSRtRR4VOJou3H66E38UjbjvDGh3slJzPuolsb+yQFqwHNNdyp5jg3w==", + "dev": true + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9922,6 +10057,12 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -10364,6 +10505,15 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10457,6 +10607,15 @@ "ipaddr.js": "1.9.1" } }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -10930,6 +11089,15 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "requires": { + "through": "2" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10945,27 +11113,21 @@ "escape-string-regexp": "^2.0.0" } }, - "stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "requires": { - "type-fest": "^0.7.1" - }, - "dependencies": { - "type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==" - } - } - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -11140,6 +11302,26 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "temp-fs": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", + "integrity": "sha512-WfecDCR1xC9b0nsrzSaxPf3ZuWeWLUWblW4vlDQAa1biQaKHiImHnJfeQocQe/hXKMcolRzgkcVX/7kK4zoWbw==", + "dev": true, + "requires": { + "rimraf": "~2.5.2" + }, + "dependencies": { + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha512-Lw7SHMjssciQb/rRz7JyPIy9+bbUshEucPoLRvWqy09vC5zQixl8Uet+Zl+SROBB/JMWHJRdCk1qdxNWHNMvlQ==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + } + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -11179,6 +11361,12 @@ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/packages/js/package.json b/packages/js/package.json index 2f749b1ec..fe7cf8ef3 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -43,10 +43,7 @@ "test": "npm run test:browser && npm run test:server && npm run tsd", "test:browser": "jest --env=jsdom --setupFiles=\"/test/unit/jest-browser-setup.js\" --testPathPattern=\"test/unit/.*(? { + const combination = name.split(/@browserstack/)[0]; + const [browserCaps, osCaps] = combination.split(/:/); + const [browser, browser_version] = browserCaps.split(/@/); + const osCapsSplit = osCaps.split(/ /); + const os = osCapsSplit.shift(); + const os_version = osCapsSplit.join(' '); + caps.browser = browser ? browser : 'chrome'; + caps.os_version = browser_version ? browser_version : 'latest'; + caps.os = os ? os : 'osx'; + caps.os_version = os_version ? os_version : 'catalina'; + caps.name = title; +}; + +export function getCdpEndpoint(name: string, title: string){ + patchCaps(name, title) + const cdpUrl = `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}` + console.log(`--> ${cdpUrl}`) + return cdpUrl; +} diff --git a/packages/js/test/e2e/global-setup.ts b/packages/js/test/e2e/global-setup.ts new file mode 100644 index 000000000..e06e3946d --- /dev/null +++ b/packages/js/test/e2e/global-setup.ts @@ -0,0 +1,29 @@ +// global-setup.js +import { bsLocal, BS_LOCAL_ARGS } from './browserstack.config' +import { promisify } from 'util'; + +const sleep = promisify(setTimeout); +const redColour = '\x1b[31m'; +const whiteColour = '\x1b[0m'; +module.exports = async () => { + if (!process.env.BROWSERSTACK_ACCESS_KEY) { + return + } + + console.log('Starting BrowserStackLocal ...'); + // Starts the Local instance with the required arguments + let localResponseReceived = false; + bsLocal.start(BS_LOCAL_ARGS, (err) => { + if (err) { + console.error( + `${redColour}Error starting BrowserStackLocal${whiteColour}` + ); + } else { + console.log('BrowserStackLocal Started'); + } + localResponseReceived = true; + }); + while (!localResponseReceived) { + await sleep(1000); + } +}; diff --git a/packages/js/test/e2e/global-teardown.ts b/packages/js/test/e2e/global-teardown.ts new file mode 100644 index 000000000..27dae8592 --- /dev/null +++ b/packages/js/test/e2e/global-teardown.ts @@ -0,0 +1,19 @@ +// global-teardown.js +import { bsLocal } from './browserstack.config'; +import { promisify } from 'util'; + +const sleep = promisify(setTimeout); +module.exports = async () => { + // Stop the Local instance after your test run is completed, i.e after driver.quit + let localStopped = false; + + if (bsLocal && bsLocal.isRunning()) { + bsLocal.stop(() => { + localStopped = true; + console.log('Stopped BrowserStackLocal'); + }); + while (!localStopped) { + await sleep(1000); + } + } +} diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index 4b213d350..23df58418 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, Page } from '@playwright/test' +import { test, expect, Page, TestInfo } from '@playwright/test' import { resolve } from 'path' import type Honeybadger from '../../honeybadger' import type { NoticeTransportPayload } from '../../../core/src/types' @@ -19,11 +19,36 @@ const triggerException = async (page: Page) => { await page.click('button#throw-exception') } +const isRunningOnBrowserStack = (testInfo: TestInfo) => { + return testInfo.project.name.includes('browserstack') +} + test.describe('Browser Integration', () => { - test.beforeEach(async ({ page }, _testInfo) => { + test.beforeEach(async ({ page }, testInfo) => { + if (isRunningOnBrowserStack(testInfo)) { + const data = { + action: 'setSessionName', + arguments: { name: testInfo.project.name } + } + await page.evaluate(_ => {},`browserstack_executor: ${JSON.stringify(data)}`); + } await setup(page) }) + test.afterEach(async ({ page }, testInfo) => { + if (isRunningOnBrowserStack(testInfo)) { + const isPassed = testInfo.status === testInfo.expectedStatus + const data = { + action: 'setSessionStatus', + arguments: { + status: isPassed ? 'passed' : 'failed', + reason: isPassed ? testInfo.title : testInfo.errors.map(e => e.message).join('\n') + } + } + await page.evaluate(_ => {}, `browserstack_executor: ${JSON.stringify(data)}`); + } + }) + test('it logs browser type and version', async ({ page }) => { const context = page.context() const browser = context.browser() diff --git a/packages/js/playwright.config.ts b/packages/js/test/e2e/playwright.config.ts similarity index 51% rename from packages/js/playwright.config.ts rename to packages/js/test/e2e/playwright.config.ts index c8bc389af..54b7218c8 100644 --- a/packages/js/playwright.config.ts +++ b/packages/js/test/e2e/playwright.config.ts @@ -1,16 +1,14 @@ -import { defineConfig, devices } from '@playwright/test'; - -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// require('dotenv').config(); +import { defineConfig } from '@playwright/test' +import { browsers } from './browsers' /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './test/e2e', + testDir: '.', + globalSetup: require.resolve('./global-setup.ts'), + globalTeardown: require.resolve('./global-teardown.ts'), + /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -31,46 +29,11 @@ export default defineConfig({ }, /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, - - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, - - /* Test against branded browsers. */ - { - name: 'Microsoft Edge', - use: { ...devices['Desktop Edge'], channel: 'msedge' }, - }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, - ], + projects: browsers, /* Run your local dev server before starting the tests */ webServer: { - command: 'node test/e2e/server.js', + command: 'node ./server.js', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, From 25188b8ea1ae858021aa76d38b787f69eb509374 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 28 Aug 2023 20:16:10 +0300 Subject: [PATCH 07/21] chore: specify chrome and edge on browserstack browsers --- packages/js/test/e2e/browsers.ts | 70 +++++++++++---------- packages/js/test/e2e/browserstack.config.ts | 38 +++++------ 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index f04131348..4a89c589d 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -7,21 +7,39 @@ import { devices } from '@playwright/test'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { getCdpEndpoint } from './browserstack.config'; -export const browsers = [ - // { - // // Chrome minimum version - // name: 'browserstack_chrome_83_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_49_windows') }, - // }, - // }, - // { - // // Chrome latest version - // name: 'browserstack_chrome_latest_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, - // }, - // }, +const browserStackBrowsers = [ + { + // Chrome minimum version + // Earliest available chrome version is 83 + // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + name: 'browserstack_chrome_83_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, + }, + }, + { + // Chrome latest version + name: 'browserstack_chrome_latest_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, + }, + }, + { + // Edge minimum version + // Earliest available chrome version is 83 + // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + name: 'browserstack_edge_83_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, + }, + }, + { + // Edge latest version + name: 'browserstack_edge_latest_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, + }, + }, // { // // Safari minimum version // base: 'BrowserStack', @@ -54,24 +72,9 @@ export const browsers = [ // os: 'Windows', // os_version: '10' // }, - // { - // // Edge minimum version - // base: 'BrowserStack', - // browser: 'Edge', - // browser_version: '15', - // os: 'Windows', - // os_version: '10' - // }, - // { - // // Edge latest version - // base: 'BrowserStack', - // browser: 'Edge', - // browser_version: 'latest', - // os: 'Windows', - // os_version: '11' - // }, +] - // Uncomment below to test with playwright's bundled browsers +const playwrightBrowsers = [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, @@ -105,3 +108,6 @@ export const browsers = [ // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // }, ] + +// eslint-disable-next-line +export const browsers = !!process.env.CI ? browserStackBrowsers : playwrightBrowsers diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index cd6eaf5b9..59568a101 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -5,19 +5,20 @@ const clientPlaywrightVersion = cp .execSync('npx playwright --version') .toString() .trim() - .split(' ')[1]; + .split(' ')[1] -export const bsLocal = new BrowserStackLocal.Local(); +export const bsLocal = new BrowserStackLocal.Local() // replace YOUR_ACCESS_KEY with your key. You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY". export const BS_LOCAL_ARGS = { key: process.env.BROWSERSTACK_ACCESS_KEY, -}; +} // BrowserStack Specific Capabilities. // Set 'browserstack.local:true For Local testing const caps = { browser: 'chrome', + browser_version: 'latest', os: 'osx', os_version: 'catalina', name: 'Honeybadger Integration Tests', @@ -26,26 +27,25 @@ const caps = { 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, 'browserstack.local': process.env.BROWSERSTACK_LOCAL || true, 'client.playwrightVersion': clientPlaywrightVersion, -}; +} // Patching the capabilities dynamically according to the project name. const patchCaps = (name: string, title: string) => { - const combination = name.split(/@browserstack/)[0]; - const [browserCaps, osCaps] = combination.split(/:/); - const [browser, browser_version] = browserCaps.split(/@/); - const osCapsSplit = osCaps.split(/ /); - const os = osCapsSplit.shift(); - const os_version = osCapsSplit.join(' '); - caps.browser = browser ? browser : 'chrome'; - caps.os_version = browser_version ? browser_version : 'latest'; - caps.os = os ? os : 'osx'; - caps.os_version = os_version ? os_version : 'catalina'; - caps.name = title; -}; + const combination = name.split(/@browserstack/)[0] + const [browserCaps, osCaps] = combination.split(/:/) + const [browser, browser_version] = browserCaps.split(/@/) + const osCapsSplit = osCaps.split(/ /) + const os = osCapsSplit.shift() + const os_version = osCapsSplit.join(' ') + caps.browser = browser ? browser : 'chrome' + caps.browser_version = browser_version ? browser_version : 'latest' + caps.os = os ? os : 'osx' + caps.os_version = os_version ? os_version : 'catalina' + caps.name = title +} export function getCdpEndpoint(name: string, title: string){ patchCaps(name, title) - const cdpUrl = `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}` - console.log(`--> ${cdpUrl}`) - return cdpUrl; + return `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}` + // console.log(`--> ${cdpUrl}`) } From 6fc4a80b52a6fd2e9bf6e19675967fc6f9682c40 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 28 Aug 2023 21:28:53 +0300 Subject: [PATCH 08/21] chore: browserstack secrets on ci --- .github/workflows/node.js.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index e88d67b19..f6000cda6 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -90,6 +90,9 @@ jobs: - name: Run integration tests env: CI: true + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + BROWSERSTACK_LOCAL: false run: | cd packages/js npm run test:integration From 389ea52250f17f501fbf5cde169b977014af08e6 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 09:35:46 +0300 Subject: [PATCH 09/21] chore: debug browserstack not working --- packages/js/test/e2e/browsers.ts | 46 +++++++++++------------ packages/js/test/e2e/playwright.config.ts | 2 + 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 4a89c589d..464311129 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -17,29 +17,29 @@ const browserStackBrowsers = [ connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, }, }, - { - // Chrome latest version - name: 'browserstack_chrome_latest_windows', - use: { - connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, - }, - }, - { - // Edge minimum version - // Earliest available chrome version is 83 - // https://www.browserstack.com/docs/automate/playwright/browsers-and-os - name: 'browserstack_edge_83_windows', - use: { - connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, - }, - }, - { - // Edge latest version - name: 'browserstack_edge_latest_windows', - use: { - connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, - }, - }, + // { + // // Chrome latest version + // name: 'browserstack_chrome_latest_windows', + // use: { + // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, + // }, + // }, + // { + // // Edge minimum version + // // Earliest available chrome version is 83 + // // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + // name: 'browserstack_edge_83_windows', + // use: { + // connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, + // }, + // }, + // { + // // Edge latest version + // name: 'browserstack_edge_latest_windows', + // use: { + // connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, + // }, + // }, // { // // Safari minimum version // base: 'BrowserStack', diff --git a/packages/js/test/e2e/playwright.config.ts b/packages/js/test/e2e/playwright.config.ts index 54b7218c8..dec2267e5 100644 --- a/packages/js/test/e2e/playwright.config.ts +++ b/packages/js/test/e2e/playwright.config.ts @@ -36,5 +36,7 @@ export default defineConfig({ command: 'node ./server.js', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, + stdout: 'pipe', + stderr: 'pipe', }, }); From c01c4fdda33b4312d774366728bdcf6601f2341a Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 09:41:12 +0300 Subject: [PATCH 10/21] chore: enable network logs --- .github/workflows/node.js.yml | 2 +- packages/js/test/e2e/browserstack.config.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index f6000cda6..36554eb2e 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -84,7 +84,7 @@ jobs: - name: Build run: npm ci - - name: Instal; Playwright Browsers + - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run integration tests diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index 59568a101..766e7dc2a 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -26,6 +26,7 @@ const caps = { 'browserstack.username': process.env.BROWSERSTACK_USERNAME, 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, 'browserstack.local': process.env.BROWSERSTACK_LOCAL || true, + 'browserstack.networkLogs': true, 'client.playwrightVersion': clientPlaywrightVersion, } From b8aaeaeb68870510fecfadfbb66f19522fafa3d9 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 09:47:30 +0300 Subject: [PATCH 11/21] chore: set browserstack local to true --- packages/js/test/e2e/browserstack.config.ts | 2 +- packages/js/test/e2e/global-setup.ts | 19 ++++++++++--------- packages/js/test/e2e/global-teardown.ts | 5 ++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index 766e7dc2a..133d0ce7d 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -25,7 +25,7 @@ const caps = { build: 'honeybadger-playwright-browserstack', 'browserstack.username': process.env.BROWSERSTACK_USERNAME, 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, - 'browserstack.local': process.env.BROWSERSTACK_LOCAL || true, + 'browserstack.local': true, // we are running a local web server, so we need this to be true 'browserstack.networkLogs': true, 'client.playwrightVersion': clientPlaywrightVersion, } diff --git a/packages/js/test/e2e/global-setup.ts b/packages/js/test/e2e/global-setup.ts index e06e3946d..52f8369cd 100644 --- a/packages/js/test/e2e/global-setup.ts +++ b/packages/js/test/e2e/global-setup.ts @@ -1,12 +1,13 @@ -// global-setup.js import { bsLocal, BS_LOCAL_ARGS } from './browserstack.config' -import { promisify } from 'util'; +import { promisify } from 'util' -const sleep = promisify(setTimeout); -const redColour = '\x1b[31m'; -const whiteColour = '\x1b[0m'; +const sleep = promisify(setTimeout) +const redColour = '\x1b[31m' +const whiteColour = '\x1b[0m' module.exports = async () => { if (!process.env.BROWSERSTACK_ACCESS_KEY) { + console.log('Will not start BrowserStackLocal because BROWSERSTACK_ACCESS_KEY is not set') + return } @@ -17,13 +18,13 @@ module.exports = async () => { if (err) { console.error( `${redColour}Error starting BrowserStackLocal${whiteColour}` - ); + ) } else { - console.log('BrowserStackLocal Started'); + console.log('BrowserStackLocal Started') } localResponseReceived = true; }); while (!localResponseReceived) { - await sleep(1000); + await sleep(1000) } -}; +} diff --git a/packages/js/test/e2e/global-teardown.ts b/packages/js/test/e2e/global-teardown.ts index 27dae8592..c911714d9 100644 --- a/packages/js/test/e2e/global-teardown.ts +++ b/packages/js/test/e2e/global-teardown.ts @@ -1,8 +1,7 @@ -// global-teardown.js import { bsLocal } from './browserstack.config'; import { promisify } from 'util'; -const sleep = promisify(setTimeout); +const sleep = promisify(setTimeout) module.exports = async () => { // Stop the Local instance after your test run is completed, i.e after driver.quit let localStopped = false; @@ -10,7 +9,7 @@ module.exports = async () => { if (bsLocal && bsLocal.isRunning()) { bsLocal.stop(() => { localStopped = true; - console.log('Stopped BrowserStackLocal'); + console.log('Stopped BrowserStackLocal') }); while (!localStopped) { await sleep(1000); From 51238ead4c70bc665d40d7394e17cf08e6eb5fdc Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 09:59:43 +0300 Subject: [PATCH 12/21] chore: trigger e2e --- .github/workflows/node.js.yml | 1 - packages/js/test/e2e/browserstack.config.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 36554eb2e..34142eedf 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -92,7 +92,6 @@ jobs: CI: true BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - BROWSERSTACK_LOCAL: false run: | cd packages/js npm run test:integration diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index 133d0ce7d..2128651cb 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -15,7 +15,6 @@ export const BS_LOCAL_ARGS = { } // BrowserStack Specific Capabilities. -// Set 'browserstack.local:true For Local testing const caps = { browser: 'chrome', browser_version: 'latest', From df0c0b8629c8ab66f66ff74d75d4f9deb456d8cb Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 18:32:52 +0300 Subject: [PATCH 13/21] chore: run on latest chrome --- packages/js/test/e2e/browsers.ts | 14 +++++++------- packages/js/test/e2e/integration.spec.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 464311129..34849bf49 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -17,13 +17,13 @@ const browserStackBrowsers = [ connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, }, }, - // { - // // Chrome latest version - // name: 'browserstack_chrome_latest_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, - // }, - // }, + { + // Chrome latest version + name: 'browserstack_chrome_latest_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, + }, + }, // { // // Edge minimum version // // Earliest available chrome version is 83 diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index 23df58418..e6246fbf0 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -42,7 +42,7 @@ test.describe('Browser Integration', () => { action: 'setSessionStatus', arguments: { status: isPassed ? 'passed' : 'failed', - reason: isPassed ? testInfo.title : testInfo.errors.map(e => e.message).join('\n') + reason: isPassed ? testInfo.title : (`${testInfo.title}[retry:${testInfo.retry}] | ` + testInfo.errors.map(e => e.message).join('\n')) } } await page.evaluate(_ => {}, `browserstack_executor: ${JSON.stringify(data)}`); From a09533dbb8ee6abcc64348024fab2641b3223109 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 18:34:43 +0300 Subject: [PATCH 14/21] chore: dummy commit --- packages/js/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/js/.gitignore b/packages/js/.gitignore index 88a466cdb..7b504295e 100644 --- a/packages/js/.gitignore +++ b/packages/js/.gitignore @@ -3,3 +3,4 @@ node_modules/ /test-results/ /playwright-report/ /playwright/.cache/ +dummy-commit-to-trigger-ci From 16e7a3911983b0ddef63e9c2aaea74d7396a861c Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Thu, 7 Sep 2023 20:04:40 +0300 Subject: [PATCH 15/21] chore: run playwright browsers --- packages/js/test/e2e/browsers.ts | 70 +++++++++++---------- packages/js/test/e2e/browserstack.config.ts | 2 + packages/js/test/e2e/playwright.config.ts | 3 +- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 34849bf49..156898394 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -7,16 +7,17 @@ import { devices } from '@playwright/test'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { getCdpEndpoint } from './browserstack.config'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars const browserStackBrowsers = [ - { - // Chrome minimum version - // Earliest available chrome version is 83 - // https://www.browserstack.com/docs/automate/playwright/browsers-and-os - name: 'browserstack_chrome_83_windows', - use: { - connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, - }, - }, + // { + // // Chrome minimum version + // // Earliest available chrome version is 83 + // // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + // name: 'browserstack_chrome_83_windows', + // use: { + // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, + // }, + // }, { // Chrome latest version name: 'browserstack_chrome_latest_windows', @@ -79,35 +80,36 @@ const playwrightBrowsers = [ name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, - // { - // name: 'firefox', - // use: { ...devices['Desktop Firefox'] }, - // }, - // { - // name: 'webkit', - // use: { ...devices['Desktop Safari'] }, - // }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, + { + name: 'Mobile Chrome', + use: { ...devices['Pixel 5'] }, + }, + { + name: 'Mobile Safari', + use: { ...devices['iPhone 12'] }, + }, /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, + { + name: 'Microsoft Edge', + use: { ...devices['Desktop Edge'], channel: 'msedge' }, + }, + { + name: 'Google Chrome', + use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + }, ] // eslint-disable-next-line -export const browsers = !!process.env.CI ? browserStackBrowsers : playwrightBrowsers +// export const browsers = (!!process.env.CI || !!process.env.BROWSERSTACK_ACCESS_KEY) ? browserStackBrowsers : playwrightBrowsers +export const browsers = playwrightBrowsers diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index 2128651cb..d129add5c 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -26,6 +26,8 @@ const caps = { 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, 'browserstack.local': true, // we are running a local web server, so we need this to be true 'browserstack.networkLogs': true, + 'browserstack.debug': true, // visual logs + 'browserstack.console': 'verbose', // console logs 'client.playwrightVersion': clientPlaywrightVersion, } diff --git a/packages/js/test/e2e/playwright.config.ts b/packages/js/test/e2e/playwright.config.ts index dec2267e5..0fd3a9b06 100644 --- a/packages/js/test/e2e/playwright.config.ts +++ b/packages/js/test/e2e/playwright.config.ts @@ -15,8 +15,7 @@ export default defineConfig({ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + workers: 8, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ From 3275fc913f004b5fbff71134ee9a675691865649 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Fri, 8 Sep 2023 13:05:16 +0300 Subject: [PATCH 16/21] chore: run both browserstack and playwright --- packages/js/test/e2e/browsers.ts | 88 +- packages/js/test/e2e/integration.spec.ts | 6 +- packages/js/test/integration/.gitignore | 2 - packages/js/test/integration/browsers.js | 62 - packages/js/test/integration/karma.conf.js | 75 - .../js/test/integration/package-lock.json | 3947 ----------------- packages/js/test/integration/package.json | 17 - packages/js/test/integration/sandbox.html | 37 - packages/js/test/integration/test.js | 555 --- packages/js/test/integration/worker.js | 20 - 10 files changed, 30 insertions(+), 4779 deletions(-) delete mode 100644 packages/js/test/integration/.gitignore delete mode 100644 packages/js/test/integration/browsers.js delete mode 100644 packages/js/test/integration/karma.conf.js delete mode 100644 packages/js/test/integration/package-lock.json delete mode 100644 packages/js/test/integration/package.json delete mode 100644 packages/js/test/integration/sandbox.html delete mode 100644 packages/js/test/integration/test.js delete mode 100644 packages/js/test/integration/worker.js diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 156898394..6693f325a 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -9,15 +9,15 @@ import { getCdpEndpoint } from './browserstack.config'; // eslint-disable-next-line @typescript-eslint/no-unused-vars const browserStackBrowsers = [ - // { - // // Chrome minimum version - // // Earliest available chrome version is 83 - // // https://www.browserstack.com/docs/automate/playwright/browsers-and-os - // name: 'browserstack_chrome_83_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, - // }, - // }, + { + // Chrome minimum version + // Earliest available chrome version is 83 + // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + name: 'browserstack_chrome_83_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, + }, + }, { // Chrome latest version name: 'browserstack_chrome_latest_windows', @@ -25,54 +25,22 @@ const browserStackBrowsers = [ connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, }, }, - // { - // // Edge minimum version - // // Earliest available chrome version is 83 - // // https://www.browserstack.com/docs/automate/playwright/browsers-and-os - // name: 'browserstack_edge_83_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, - // }, - // }, - // { - // // Edge latest version - // name: 'browserstack_edge_latest_windows', - // use: { - // connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, - // }, - // }, - // { - // // Safari minimum version - // base: 'BrowserStack', - // browser: 'Safari', - // browser_version: '12.1', - // os: 'OS X', - // os_version: 'Mojave' - // }, - // { - // // Safari latest version - // base: 'BrowserStack', - // browser: 'Safari', - // browser_version: 'latest', - // os: 'OS X', - // os_version: 'Ventura' - // }, - // { - // // Firefox minimum version - // base: 'BrowserStack', - // browser: 'Firefox', - // browser_version: '58', - // os: 'Windows', - // os_version: '10' - // }, - // { - // // Firefox latest version - // base: 'BrowserStack', - // browser: 'Firefox', - // browser_version: 'latest', - // os: 'Windows', - // os_version: '10' - // }, + { + // Edge minimum version + // Earliest available chrome version is 83 + // https://www.browserstack.com/docs/automate/playwright/browsers-and-os + name: 'browserstack_edge_83_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, + }, + }, + { + // Edge latest version + name: 'browserstack_edge_latest_windows', + use: { + connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, + }, + }, ] const playwrightBrowsers = [ @@ -110,6 +78,6 @@ const playwrightBrowsers = [ }, ] -// eslint-disable-next-line -// export const browsers = (!!process.env.CI || !!process.env.BROWSERSTACK_ACCESS_KEY) ? browserStackBrowsers : playwrightBrowsers -export const browsers = playwrightBrowsers +export const browsers = (!!process.env.CI || !!process.env.BROWSERSTACK_ACCESS_KEY) + ? [...browserStackBrowsers, ...playwrightBrowsers] + : playwrightBrowsers diff --git a/packages/js/test/e2e/integration.spec.ts b/packages/js/test/e2e/integration.spec.ts index e6246fbf0..469206f2b 100644 --- a/packages/js/test/e2e/integration.spec.ts +++ b/packages/js/test/e2e/integration.spec.ts @@ -274,12 +274,10 @@ test.describe('Browser Integration', () => { expect(notices.length).toEqual(1) }) - test('it sends window.onunhandledrejection breadcrumbs when rejection is an Error', async ({ page }) => { - // let errorStack = null - // page.on('pageerror', (err) => errorStack = err.stack) + test('it sends window.onunhandledrejection breadcrumbs when rejection is an Error', async ({ page }, testInfo) => { const resultHandle = await page.evaluateHandle(_ => !('onunhandledrejection' in window)) const doesNotSupportUnhandledRejectionListener = await resultHandle.jsonValue() - test.skip(doesNotSupportUnhandledRejectionListener, 'onunhandledrejection not supported') + test.skip(doesNotSupportUnhandledRejectionListener || ['browserstack_chrome_83_windows', 'browserstack_edge_83_windows'].includes(testInfo.project.name), 'onunhandledrejection not supported') const handle = await page.evaluateHandle(_ => { const myPromise = new Promise(() => { diff --git a/packages/js/test/integration/.gitignore b/packages/js/test/integration/.gitignore deleted file mode 100644 index 408be3f8f..000000000 --- a/packages/js/test/integration/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -browserstack.err -local.log diff --git a/packages/js/test/integration/browsers.js b/packages/js/test/integration/browsers.js deleted file mode 100644 index 8a5b20889..000000000 --- a/packages/js/test/integration/browsers.js +++ /dev/null @@ -1,62 +0,0 @@ -// When updating the minimum versions we test against in this file, -// please also update the docs: -// https://github.com/honeybadger-io/docs/blob/master/source/lib/javascript/reference/supported-versions.html.md - -module.exports = { - bs_safari_min: { - base: 'BrowserStack', - browser: 'Safari', - browser_version: '12.1', - os: 'OS X', - os_version: 'Mojave' - }, - bs_safari_latest: { - base: 'BrowserStack', - browser: 'Safari', - browser_version: 'latest', - os: 'OS X', - os_version: 'Ventura' - }, - bs_chrome_min: { - base: 'BrowserStack', - browser: 'Chrome', - browser_version: '49', - os: 'Windows', - os_version: '10' - }, - bs_chrome_latest: { - base: 'BrowserStack', - browser: 'Chrome', - browser_version: 'latest', - os: 'Windows', - os_version: '11' - }, - bs_firefox_min: { - base: 'BrowserStack', - browser: 'Firefox', - browser_version: '58', - os: 'Windows', - os_version: '10' - }, - bs_firefox_latest: { - base: 'BrowserStack', - browser: 'Firefox', - browser_version: 'latest', - os: 'Windows', - os_version: '10' - }, - bs_edge_min: { - base: 'BrowserStack', - browser: 'Edge', - browser_version: '15', - os: 'Windows', - os_version: '10' - }, - bs_edge_latest: { - base: 'BrowserStack', - browser: 'Edge', - browser_version: 'latest', - os: 'Windows', - os_version: '11' - } -} diff --git a/packages/js/test/integration/karma.conf.js b/packages/js/test/integration/karma.conf.js deleted file mode 100644 index a4a3381f7..000000000 --- a/packages/js/test/integration/karma.conf.js +++ /dev/null @@ -1,75 +0,0 @@ -// I'm getting local timeout errors when this is enabled -// process.env.CHROME_BIN = require('puppeteer').executablePath() - -const availableBrowsers = require('./browsers.js') -let browsers = {} -if (process.env.BROWSERS) { - process.env.BROWSERS.split(/\s*,\s*/).forEach((key) => { - if (key in availableBrowsers) { - browsers[key] = availableBrowsers[key] - } - }) -} else { - browsers = availableBrowsers -} - -module.exports = function (config) { - config.set({ - port: 9876, - basePath: '../../', - colors: true, - autoWatch: false, - singleRun: true, - logLevel: config.LOG_INFO, - files: [ - // Polyfills for IE 11 - 'test/integration/node_modules/promise-polyfill/dist/polyfill.js', - 'test/integration/node_modules/whatwg-fetch/dist/fetch.umd.js', - - // The test file - 'test/integration/test.js', - - // Web Worker - { pattern: 'test/integration/worker.js', included: false }, - - // File being tested - { pattern: 'dist/browser/honeybadger.js', included: false }, - - // Integration sandbox - { pattern: 'test/integration/sandbox.html', included: false }, - - // User Feedback Form asset - { pattern: 'dist/browser/honeybadger-feedback-form.js', included: false }, - ], - frameworks: ['jasmine', 'jasmine-matchers'], - }) - - if (process.env.HEADLESS === '1') { - config.set({ - reporters: ['dots'], - browsers: ['ChromeHeadless'], - }) - } else { - if (Object.keys(browsers).length === 0) { - console.warn('No valid browsers detected; exiting.'); - process.exit(); - } - - config.set({ - customLaunchers: browsers, - browsers: Object.keys(browsers), - reporters: ['dots', 'BrowserStack'], - browserStack: { - project: 'honeybadger-universal-js', - build: 'integration' - }, - hostname: 'bs-local.com', - concurrency: 2, - browserDisconnectTolerance: 5, - retryLimit: 5, - browserSocketTimeout: 120000, - browserNoActivityTimeout: 120000, - captureTimeout: 120000 - }) - } -} diff --git a/packages/js/test/integration/package-lock.json b/packages/js/test/integration/package-lock.json deleted file mode 100644 index df0d5c8eb..000000000 --- a/packages/js/test/integration/package-lock.json +++ /dev/null @@ -1,3947 +0,0 @@ -{ - "name": "integration", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "devDependencies": { - "jasmine-core": "^3.6.0", - "jasmine-expect": "^4.0.3", - "karma": "^6.3.16", - "karma-browserstack-launcher": "^1.6.0", - "karma-chrome-launcher": "^3.1.0", - "karma-jasmine": "^4.0.1", - "karma-jasmine-matchers": "^4.0.2", - "promise-polyfill": "^8.1.3", - "puppeteer": "^5.2.1", - "whatwg-fetch": "^3.4.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "14.6.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.3.tgz", - "integrity": "sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/add-matchers": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/add-matchers/-/add-matchers-0.6.2.tgz", - "integrity": "sha512-hVO2wodMei9RF00qe+506MoeJ/NEOdCMEkSJ12+fC3hx/5Z4zmhNiP92nJEF6XhmXokeB0hOtuQrjHCx2vmXrQ==", - "dev": true - }, - "node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, - "node_modules/browserstack-local": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.7.tgz", - "integrity": "sha512-wZtMa/CDT+Vf7wZrK8zGkWcScaenC4lfLa6RQkHZX/OnP1b4ZDWuE0GevMf2/pe5i8thfzIkCaLhsJnanqql9A==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^4.0.0", - "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" - } - }, - "node_modules/browserstack-local/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/browserstack-local/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "node_modules/date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.781568", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.781568.tgz", - "integrity": "sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==", - "dev": true - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-running": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", - "dev": true - }, - "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "dev": true, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "node_modules/jasmine-expect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-4.0.3.tgz", - "integrity": "sha512-ucHOO6qpY6/vwgxoRVzyYywIRuvGEmI1uk69INjdZ84h2dUHIXHf8A5jUkM1kQHy9lHP1jqq9/S5VAkYUsFYCg==", - "dev": true, - "dependencies": { - "add-matchers": "0.6.2" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/karma": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", - "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", - "dev": true, - "dependencies": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.2.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-browserstack-launcher": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.6.0.tgz", - "integrity": "sha512-Y/UWPdHZkHIVH2To4GWHCTzmrsB6H7PBWy6pw+TWz5sr4HW2mcE+Uj6qWgoVNxvQU1Pfn5LQQzI6EQ65p8QbiQ==", - "dev": true, - "dependencies": { - "browserstack": "~1.5.1", - "browserstack-local": "^1.3.7", - "q": "~1.5.0" - }, - "peerDependencies": { - "karma": ">=0.9" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-chrome-launcher/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", - "dev": true, - "dependencies": { - "jasmine-core": "^3.6.0" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "karma": "*" - } - }, - "node_modules/karma-jasmine-matchers": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-matchers/-/karma-jasmine-matchers-4.0.2.tgz", - "integrity": "sha512-NJjJ0UI7TH2Nw1kEnc3uFv//TkELlq6Z+O/Z9KScl49MCItFs8xaRthZxzDEKlwKtMAclhzbFAP9f0RHtI9g1A==", - "dev": true, - "dependencies": { - "jasmine-expect": "4.0.2" - } - }, - "node_modules/karma-jasmine-matchers/node_modules/jasmine-expect": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-4.0.2.tgz", - "integrity": "sha512-VpHLwpBE1chVIhiJ7kJHBbdsm2GVvBli5bem4pGqkbiuvIW2mxghPNhYrKyoHBQKVmlq+xLUTGlrWMC/Ovn+2g==", - "dev": true, - "dependencies": { - "add-matchers": "0.6.2" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", - "dev": true, - "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==", - "dev": true - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/puppeteer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.2.1.tgz", - "integrity": "sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.781568", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "engines": { - "node": ">=10.18.1" - } - }, - "node_modules/puppeteer/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/puppeteer/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.4.1", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "dependencies": { - "ws": "~8.11.0" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", - "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", - "dev": true, - "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar-fs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", - "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "node_modules/tar-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", - "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", - "dev": true, - "dependencies": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/temp-fs": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", - "dev": true, - "dependencies": { - "rimraf": "~2.5.2" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/temp-fs/node_modules/rimraf": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, - "dependencies": { - "glob": "^7.0.5" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz", - "integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - }, - "dependencies": { - "@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "14.6.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.3.tgz", - "integrity": "sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww==", - "dev": true - }, - "@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "add-matchers": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/add-matchers/-/add-matchers-0.6.2.tgz", - "integrity": "sha512-hVO2wodMei9RF00qe+506MoeJ/NEOdCMEkSJ12+fC3hx/5Z4zmhNiP92nJEF6XhmXokeB0hOtuQrjHCx2vmXrQ==", - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "browserstack-local": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.7.tgz", - "integrity": "sha512-wZtMa/CDT+Vf7wZrK8zGkWcScaenC4lfLa6RQkHZX/OnP1b4ZDWuE0GevMf2/pe5i8thfzIkCaLhsJnanqql9A==", - "dev": true, - "requires": { - "https-proxy-agent": "^4.0.0", - "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" - }, - "dependencies": { - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true - }, - "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "requires": { - "agent-base": "5", - "debug": "4" - } - } - } - }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true - }, - "devtools-protocol": { - "version": "0.0.781568", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.781568.tgz", - "integrity": "sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==", - "dev": true - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", - "dev": true, - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" - }, - "dependencies": { - "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} - } - } - }, - "engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "dev": true - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "dependencies": { - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } - } - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-running": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", - "dev": true - }, - "isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "jasmine-expect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-4.0.3.tgz", - "integrity": "sha512-ucHOO6qpY6/vwgxoRVzyYywIRuvGEmI1uk69INjdZ84h2dUHIXHf8A5jUkM1kQHy9lHP1jqq9/S5VAkYUsFYCg==", - "dev": true, - "requires": { - "add-matchers": "0.6.2" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "karma": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", - "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", - "dev": true, - "requires": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.2.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - } - }, - "karma-browserstack-launcher": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.6.0.tgz", - "integrity": "sha512-Y/UWPdHZkHIVH2To4GWHCTzmrsB6H7PBWy6pw+TWz5sr4HW2mcE+Uj6qWgoVNxvQU1Pfn5LQQzI6EQ65p8QbiQ==", - "dev": true, - "requires": { - "browserstack": "~1.5.1", - "browserstack-local": "^1.3.7", - "q": "~1.5.0" - } - }, - "karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "requires": { - "which": "^1.2.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", - "dev": true, - "requires": { - "jasmine-core": "^3.6.0" - } - }, - "karma-jasmine-matchers": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-matchers/-/karma-jasmine-matchers-4.0.2.tgz", - "integrity": "sha512-NJjJ0UI7TH2Nw1kEnc3uFv//TkELlq6Z+O/Z9KScl49MCItFs8xaRthZxzDEKlwKtMAclhzbFAP9f0RHtI9g1A==", - "dev": true, - "requires": { - "jasmine-expect": "4.0.2" - }, - "dependencies": { - "jasmine-expect": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-4.0.2.tgz", - "integrity": "sha512-VpHLwpBE1chVIhiJ7kJHBbdsm2GVvBli5bem4pGqkbiuvIW2mxghPNhYrKyoHBQKVmlq+xLUTGlrWMC/Ovn+2g==", - "dev": true, - "requires": { - "add-matchers": "0.6.2" - } - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - } - }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==", - "dev": true - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "puppeteer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.2.1.tgz", - "integrity": "sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.781568", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "dependencies": { - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true - }, - "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "requires": { - "agent-base": "5", - "debug": "4" - } - } - } - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.4.1", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" - } - }, - "socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "requires": { - "ws": "~8.11.0" - }, - "dependencies": { - "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} - } - } - }, - "socket.io-parser": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", - "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", - "dev": true, - "requires": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, - "streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "tar-fs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", - "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "tar-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", - "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", - "dev": true, - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "temp-fs": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", - "dev": true, - "requires": { - "rimraf": "~2.5.2" - }, - "dependencies": { - "rimraf": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", - "dev": true - }, - "unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "whatwg-fetch": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz", - "integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/packages/js/test/integration/package.json b/packages/js/test/integration/package.json deleted file mode 100644 index 9ae6d7a34..000000000 --- a/packages/js/test/integration/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "scripts": { - "test": "karma start" - }, - "devDependencies": { - "jasmine-core": "^3.6.0", - "jasmine-expect": "^4.0.3", - "karma": "^6.3.16", - "karma-browserstack-launcher": "^1.6.0", - "karma-chrome-launcher": "^3.1.0", - "karma-jasmine": "^4.0.1", - "karma-jasmine-matchers": "^4.0.2", - "promise-polyfill": "^8.1.3", - "puppeteer": "^5.2.1", - "whatwg-fetch": "^3.4.0" - } -} diff --git a/packages/js/test/integration/sandbox.html b/packages/js/test/integration/sandbox.html deleted file mode 100644 index 10c522097..000000000 --- a/packages/js/test/integration/sandbox.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - Integration Sandbox - - - - - - - - - - -
- -
- anchor text - - diff --git a/packages/js/test/integration/test.js b/packages/js/test/integration/test.js deleted file mode 100644 index 230e9d002..000000000 --- a/packages/js/test/integration/test.js +++ /dev/null @@ -1,555 +0,0 @@ -/* eslint-disable */ -const isIE = window.document.documentMode; - -/** - * Creates a new integration sandbox. - * @param {function} callback Invoked when the sandbox is ready. - * @return {!HTMLElement} The sandbox object. -*/ -function createSandbox(callback) { - const sandbox = document.createElement('iframe'); - - sandbox.style.display = 'none'; - sandbox.src = '/base/test/integration/sandbox.html'; - sandbox.onload = callback; - - function sandboxEval(code) { - sandbox.contentWindow.eval( - 'setTimeout(function() {' + - 'var func = ' + code.toString() + ';' + - 'func(window.report);' + - '});' - ); - } - - // Use `sandbox.run(function() {})` to execute the function inside - // the sandboxed environment. - sandbox.run = function (code) { - let resolve; - const promise = new Promise(function (r) { - resolve = r; - }); - - sandbox.contentWindow.reportResults = function (results) { - resolve(results); - }; - - const report = function () { - setTimeout(function () { - window.reportResults(results); - }, 50); - }; - - sandbox.contentWindow.report = function () { - sandboxEval(report); - }; - - sandboxEval(code); - - // If the code expects an argument, it is responsible to report. - if (code.length === 0) { - sandboxEval(report); - } - - return promise; - }; - - // Use `sandbox.destroy()` to stop using the sandbox. - sandbox.destroy = function () { - document.body.removeChild(sandbox); - }; - - document.body.appendChild(sandbox); - - return sandbox; -} - - -describe('browser integration', function () { - let sandbox; - - beforeEach(function (done) { - sandbox = createSandbox(done); - }); - - afterEach(function () { - sandbox.destroy(); - }); - - it('notifies Honeybadger of unhandled exceptions', function (done) { - sandbox - .run(function () { - throw new Error('unhandled exception'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].error.message).toEqual('unhandled exception'); - done(); - }) - .catch(done); - }); - - it('notifies Honeybadger manually', function (done) { - sandbox - .run(function () { - Honeybadger.notify('expected message'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].error.message).toEqual('expected message'); - done(); - }) - .catch(done); - }); - - it('reports multiple errors in the same process', function (done) { - sandbox - .run(function () { - Honeybadger.notify('expected message 1'); - Honeybadger.notify('expected message 2'); - throw new Error('unhandled exception'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(3); - done(); - }) - .catch(done); - }); - - it('skips onunhandledrejection when already sent', function (done) { - if (!('onunhandledrejection' in window)) { pending(); } - - sandbox - .run(function () { - const promise = new Promise(function (resolutionFunc, rejectionFunc) { - throw new Error('unhandled exception'); - }); - promise - .then(function (value) { console.log("value:", value) }) - .catch(function (err) { Honeybadger.notify(err) }); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - done(); - }) - .catch(done); - }); - - it('sends console breadcrumbs', function (done) { - sandbox - .run(function () { - console.log('expected message'); - Honeybadger.notify('testing'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('expected message'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('log'); - done(); - }) - .catch(done); - }); - - it('sends string value console breadcrumbs when null', function (done) { - sandbox - .run(function () { - console.log(null); - Honeybadger.notify('testing'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('null'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('log'); - done(); - }) - .catch(done); - }); - - it('sends click breadcrumbs', function (done) { - sandbox - .run(function () { - var button = document.getElementById('buttonId'); - button.click(); - Honeybadger.notify('testing'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('button#buttonId'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('ui.click'); - expect(results.notices[0].breadcrumbs.trail[0].metadata.selector).toEqual('body > div#buttonDivId > button#buttonId'); - expect(results.notices[0].breadcrumbs.trail[0].metadata.text).toEqual('button text'); - done(); - }) - .catch(done); - }); - - it('sends XHR breadcrumbs for relative paths', function (done) { - sandbox - .run(function (report) { - var request = new XMLHttpRequest(); - request.open('GET', '/example/path', false); - request.onreadystatechange = function () { - if (request.readyState === 4) { - Honeybadger.notify('testing'); - report(); - } - }; - request.send(null); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('request'); - expect(results.notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); - expect('message' in results.notices[0].breadcrumbs.trail[0].metadata).toBe(false); - done(); - }) - .catch(done); - }); - - it('sends XHR breadcrumbs for absolute paths', function (done) { - sandbox - .run(function (report) { - var request = new XMLHttpRequest(); - request.open('GET', 'https://example.com/example/path', true); - request.onreadystatechange = function () { - if (request.readyState === 4) { - Honeybadger.notify('testing'); - report(); - } - }; - request.send(null); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('GET https://example.com/example/path'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('request'); - expect(results.notices[0].breadcrumbs.trail[0].metadata.type).toEqual('xhr'); - expect('message' in results.notices[0].breadcrumbs.trail[0].metadata).toBe(false); - done(); - }) - .catch(done); - }); - - it('sends fetch breadcrumbs', function (done) { - sandbox - .run(function (report) { - fetch('/example/path') - .then(function () { - Honeybadger.notify('testing'); - report(); - }) - .catch(report); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('GET /example/path'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('request'); - // fetch polyfill uses XHR. - expect(results.notices[0].breadcrumbs.trail[0].metadata.type).toEqual(isIE ? 'xhr' : 'fetch'); - done(); - }) - .catch(done); - }); - - it('sends navigation breadcrumbs', function (done) { - sandbox - .run(function () { - history.pushState({}, '', 'foo.html'); - Honeybadger.notify('testing'); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(2); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('Page changed'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('navigation'); - expect(results.notices[0].breadcrumbs.trail[0].metadata).toEqual({ - // The hostname is different when running locally vs. in CI. - from: jasmine.stringMatching(/http:\/\/.+:9876\/base\/test\/integration\/sandbox\.html/), - to: 'foo.html' - }); - done(); - }) - .catch(done); - }); - - it('sends notify breadcrumbs', function (done) { - sandbox - .run(function () { - Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('notice'); - expect(results.notices[0].breadcrumbs.trail[0].metadata).toEqual(jasmine.objectContaining({ - name: 'expected name', - message: 'expected message', - stack: jasmine.any(String) - })); - expect(results.notices[0].breadcrumbs.trail[0].metadata).not.toEqual(jasmine.objectContaining({ - context: jasmine.anything() - })); - done(); - }) - .catch(done); - }); - - it('sends window.onerror breadcrumbs', function (done) { - sandbox - .run(function () { - results.error = new Error('expected message'); - throw results.error; - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail).toBeArray(); - - const errorBreadcrumbs = results.notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) - console.log(errorBreadcrumbs) - - expect(errorBreadcrumbs.length).toEqual(1) - expect(errorBreadcrumbs[0].message).toMatch('Error'); - expect(errorBreadcrumbs[0].category).toEqual('error'); - expect(errorBreadcrumbs[0].metadata).toEqual(jasmine.objectContaining({ - message: 'expected message', - name: 'Error', - stack: results.error.stack - })); - done(); - }) - .catch(done); - }); - - it('sends window.onunhandledrejection breadcrumbs when rejection is an Error', function (done) { - if (!('onunhandledrejection' in window)) { pending(); } - - sandbox - .run(function () { - results.error = new Error('expected message'); - var myPromise = new Promise(function (resolve, reject) { - reject(results.error); - }); - myPromise.then(function () { - // noop - }); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail).toBeArray(); - - const errorBreadcrumbs = results.notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) - - expect(errorBreadcrumbs.length).toEqual(1); - expect(errorBreadcrumbs[0].message).toEqual('window.onunhandledrejection: Error'); - expect(errorBreadcrumbs[0].category).toEqual('error'); - expect(errorBreadcrumbs[0].metadata).toEqual(jasmine.objectContaining({ - message: 'UnhandledPromiseRejectionWarning: Error: expected message', - name: 'Error', - stack: results.error.stack - })); - done(); - }) - .catch(done); - }); - - it('skips window.onunhandledrejection breadcrumbs when rejection is not Error', function (done) { - if (!('onunhandledrejection' in window)) { pending(); } - - sandbox - .run(function () { - var myPromise = new Promise(function (resolve, reject) { - reject('whatever'); - }); - myPromise.then(function () { - // noop - }); - }) - .then(function (results) { - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail).toBeArray(); - - const errorBreadcrumbs = results.notices[0].breadcrumbs.trail.filter(function (c) { return c.category === 'error'; }) - - expect(errorBreadcrumbs.length).toEqual(0); - done(); - }) - .catch(done); - }); - - it('shows user feedback form', function (done) { - sandbox - .run(function () { - Honeybadger.getUserFeedbackSubmitUrl = () => '/base/dist/browser/honeybadger-feedback-form.js' - - Honeybadger.afterNotify(() => { - Honeybadger.showUserFeedbackForm() - }) - - Honeybadger.notify('an error message') - }) - .then(function (results) { - expect(results.notices.length).toEqual(1) - expect(sandbox.contentWindow['honeybadgerUserFeedbackOptions'].noticeId).toEqual('test') - expect(sandbox.contentWindow.document.head.innerHTML).toMatch('') - setTimeout(() => { - const feedbackForm = sandbox.contentWindow.document.getElementById('honeybadger-feedback') - expect(feedbackForm).not.toBeNull() - const feedbackHeading = sandbox.contentWindow.document.getElementById('honeybadger-feedback-heading') - expect(feedbackHeading.innerHTML.trim()).toEqual('Care to help us fix this?') - - done() - }, 500) - }) - .catch(done) - }) - - it('shows user feedback form with custom labels', function (done) { - sandbox - .run(function () { - Honeybadger.getUserFeedbackSubmitUrl = () => '/base/dist/browser/honeybadger-feedback-form.js' - - Honeybadger.afterNotify(() => { - Honeybadger.showUserFeedbackForm({ - messages: { - heading: 'Help, Error!' - } - }) - }) - - Honeybadger.notify('an error message') - }) - .then(function (results) { - expect(results.notices.length).toEqual(1) - expect(sandbox.contentWindow['honeybadgerUserFeedbackOptions'].noticeId).toEqual('test') - expect(sandbox.contentWindow.document.head.innerHTML).toMatch('') - setTimeout(() => { - const feedbackForm = sandbox.contentWindow.document.getElementById('honeybadger-feedback') - expect(feedbackForm).not.toBeNull() - const feedbackHeading = sandbox.contentWindow.document.getElementById('honeybadger-feedback-heading') - expect(feedbackHeading.innerHTML.trim()).toEqual('Help, Error!') - - done() - }, 500) - }) - .catch(done) - }) - - it('sends user feedback for notice on submit', function (done) { - sandbox.style.display = 'block'; - sandbox.style.width = '800px'; - sandbox.style.height = '800px'; - sandbox - .run(function () { - Honeybadger.getUserFeedbackSubmitUrl = () => '/base/dist/browser/honeybadger-feedback-form.js' - - Honeybadger.afterNotify(() => { - Honeybadger.showUserFeedbackForm() - }) - - Honeybadger.notify('an error message') - }) - .then(function (results) { - expect(results.notices.length).toEqual(1) - expect(sandbox.contentWindow['honeybadgerUserFeedbackOptions'].noticeId).toEqual('test') - expect(sandbox.contentWindow.document.head.innerHTML).toMatch('') - setTimeout(() => { - const name = sandbox.contentWindow.document.getElementById('honeybadger-feedback-name') - name.value = 'integration test' - - const email = sandbox.contentWindow.document.getElementById('honeybadger-feedback-email') - email.value = 'integration-test@honeybadger.io' - - const comment = sandbox.contentWindow.document.getElementById('honeybadger-feedback-comment') - comment.value = 'ci integration comment' - - const button = sandbox.contentWindow.document.getElementById('honeybadger-feedback-submit') - button.click() - - setTimeout(() => { - const feedbackSubmitUrl = 'https://api.honeybadger.io/v2/feedback' + - '?format=js' + - `&api_key=integration_sandbox` + - `&token=test` + - `&name=${encodeURIComponent(name.value)}` + - `&email=${encodeURIComponent(email.value)}` + - `&comment=${encodeURIComponent(comment.value)}` - const form = sandbox.contentWindow.document.getElementById('honeybadger-feedback-form') - expect(form.innerHTML).toContain(``) - done() - }, 100) - }, 500) - }) - .catch(done) - }) - - it('closes user feedback form on cancel', function (done) { - sandbox - .run(function () { - Honeybadger.getUserFeedbackSubmitUrl = () => '/base/dist/browser/honeybadger-feedback-form.js' - - Honeybadger.afterNotify(() => { - Honeybadger.showUserFeedbackForm() - }) - - Honeybadger.notify('an error message') - }) - .then(function (results) { - expect(results.notices.length).toEqual(1) - expect(sandbox.contentWindow.document.head.innerHTML).toMatch('') - setTimeout(() => { - let feedbackFormWrapper = sandbox.contentWindow.document.getElementById('honeybadger-feedback-wrapper') - expect(feedbackFormWrapper).not.toBeNull() - - const button = sandbox.contentWindow.document.getElementById('honeybadger-feedback-cancel') - button.click() - - feedbackFormWrapper = sandbox.contentWindow.document.getElementById('honeybadger-feedback-wrapper') - expect(feedbackFormWrapper).toBeNull() - - done() - }, 500) - }) - .catch(done) - }) -}) - -describe("Web Worker", function () { - it('works within a web worker', function (done) { - let results = [] - - if (!window.Worker) { - console.warn("This browser does not support web workers") - return done() - } - - const MyWorker = new Worker("/base/test/integration/worker.js") - - MyWorker.onmessage = (e) => { - results = e.data - - expect(results.notices.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail.length).toEqual(1); - expect(results.notices[0].breadcrumbs.trail[0].message).toEqual('Honeybadger Notice'); - expect(results.notices[0].breadcrumbs.trail[0].category).toEqual('notice'); - expect(results.notices[0].breadcrumbs.trail[0].metadata).toEqual(jasmine.objectContaining({ - name: 'expected name', - message: 'expected message', - stack: jasmine.any(String) - })); - expect(results.notices[0].breadcrumbs.trail[0].metadata).not.toEqual(jasmine.objectContaining({ - context: jasmine.anything() - })); - done() - } - - MyWorker.postMessage("") - }) -}) diff --git a/packages/js/test/integration/worker.js b/packages/js/test/integration/worker.js deleted file mode 100644 index bca897949..000000000 --- a/packages/js/test/integration/worker.js +++ /dev/null @@ -1,20 +0,0 @@ -self.importScripts('/base/dist/browser/honeybadger.js') - -self.Honeybadger.configure({ - apiKey: 'integration_sandbox' -}); - - -const results = { notices: [] } - -// This is not a great test since it circumvents fetch...but I can't seem to come up with something better. -self.Honeybadger.__transport.send = function (options, payload) { - results.notices.push(payload); - return Promise.resolve({ statusCode: 201, body: JSON.stringify({ id: 'test' }) }) -}; - -self.Honeybadger.notify('expected message', { name: 'expected name', stack: 'expected stack' }); - -onmessage = () => { - postMessage(results) -} From 535035201cd176e7cdbdb419ccf63e74320230c3 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Fri, 8 Sep 2023 13:29:26 +0300 Subject: [PATCH 17/21] chore: typo --- packages/js/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/README.md b/packages/js/README.md index 69be20bb6..39c587764 100644 --- a/packages/js/README.md +++ b/packages/js/README.md @@ -43,7 +43,7 @@ Additionally, if you want to run the tests on Browserstack: ##### Architecture Inside `./test/e2e`, you will find a `server.js` file that runs a simple nodejs http server. This server is used to serve the test page, along with other static assets and to receive the error reports from the browser. -The server is automatically started and stopped by Playwright, as you can at the bottom of the `./test/e2e/playwright.config.ts` file. +The server is automatically started and stopped by Playwright, as you can see at the bottom of the `./test/e2e/playwright.config.ts` file. The test page is found in `./test/e2e/sandbox.html`. All tests are found in `./test/e2e/integration.spec.ts`. Two more configuration files, `./test/e2e/global-setup.ts` and `./test/e2e/global-teardown.ts` are used to start and stop From 52f122f687bfe55d6a4c509d24d01547dddd3d3c Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 11 Sep 2023 20:57:45 +0300 Subject: [PATCH 18/21] chore: stop bsLocal on SIGINT --- packages/js/test/e2e/global-teardown.ts | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/js/test/e2e/global-teardown.ts b/packages/js/test/e2e/global-teardown.ts index c911714d9..fd4a3ec60 100644 --- a/packages/js/test/e2e/global-teardown.ts +++ b/packages/js/test/e2e/global-teardown.ts @@ -1,18 +1,33 @@ -import { bsLocal } from './browserstack.config'; -import { promisify } from 'util'; +import { bsLocal } from './browserstack.config' +import { promisify } from 'util' -const sleep = promisify(setTimeout) -module.exports = async () => { - // Stop the Local instance after your test run is completed, i.e after driver.quit - let localStopped = false; +let bsLocalStopped = false +const stopBsLocal = async () => { + if (bsLocalStopped) { + return + } if (bsLocal && bsLocal.isRunning()) { bsLocal.stop(() => { - localStopped = true; + bsLocalStopped = true console.log('Stopped BrowserStackLocal') }); - while (!localStopped) { - await sleep(1000); + while (!bsLocalStopped) { + await sleep(1000) } } } + +const sleep = promisify(setTimeout) +module.exports = async () => { + await stopBsLocal() +} + +process.on('SIGINT', function() { + stopBsLocal() + .then(() => process.exit(0)) + .catch((err) => { + console.error('Error stopping BrowserStackLocal', err) + process.exit(1) + }) +}); From 07118662d8a56dc8bb393b4ee97e037407f73ed0 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 11 Sep 2023 21:12:17 +0300 Subject: [PATCH 19/21] chore: apply PR review feedback --- packages/js/test/e2e/browsers.ts | 3 --- packages/js/test/e2e/browserstack.config.ts | 3 +-- packages/js/test/e2e/global-setup.ts | 2 +- packages/js/test/e2e/global-teardown.ts | 27 +++++++++++---------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 6693f325a..37c7e4bfc 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -2,12 +2,9 @@ // please also update the docs: // https://github.com/honeybadger-io/docs/blob/master/source/lib/javascript/reference/supported-versions.html.md -// eslint-disable-next-line @typescript-eslint/no-unused-vars import { devices } from '@playwright/test'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars import { getCdpEndpoint } from './browserstack.config'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars const browserStackBrowsers = [ { // Chrome minimum version diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index d129add5c..ab07d2729 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -9,7 +9,6 @@ const clientPlaywrightVersion = cp export const bsLocal = new BrowserStackLocal.Local() -// replace YOUR_ACCESS_KEY with your key. You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY". export const BS_LOCAL_ARGS = { key: process.env.BROWSERSTACK_ACCESS_KEY, } @@ -48,6 +47,6 @@ const patchCaps = (name: string, title: string) => { export function getCdpEndpoint(name: string, title: string){ patchCaps(name, title) + return `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}` - // console.log(`--> ${cdpUrl}`) } diff --git a/packages/js/test/e2e/global-setup.ts b/packages/js/test/e2e/global-setup.ts index 52f8369cd..9e044d4cf 100644 --- a/packages/js/test/e2e/global-setup.ts +++ b/packages/js/test/e2e/global-setup.ts @@ -17,7 +17,7 @@ module.exports = async () => { bsLocal.start(BS_LOCAL_ARGS, (err) => { if (err) { console.error( - `${redColour}Error starting BrowserStackLocal${whiteColour}` + `${redColour}Error starting BrowserStackLocal${whiteColour}: ${err}` ) } else { console.log('BrowserStackLocal Started') diff --git a/packages/js/test/e2e/global-teardown.ts b/packages/js/test/e2e/global-teardown.ts index fd4a3ec60..b607de50f 100644 --- a/packages/js/test/e2e/global-teardown.ts +++ b/packages/js/test/e2e/global-teardown.ts @@ -1,24 +1,25 @@ +// This file is a modified version of the example on github: +// https://github.com/browserstack/node-js-playwright-browserstack/blob/main/global-setup.js + import { bsLocal } from './browserstack.config' -import { promisify } from 'util' let bsLocalStopped = false const stopBsLocal = async () => { - if (bsLocalStopped) { - return - } + return new Promise(resolve => { + if (bsLocalStopped) { + return resolve() + } - if (bsLocal && bsLocal.isRunning()) { - bsLocal.stop(() => { - bsLocalStopped = true - console.log('Stopped BrowserStackLocal') - }); - while (!bsLocalStopped) { - await sleep(1000) + if (bsLocal && bsLocal.isRunning()) { + bsLocal.stop(() => { + bsLocalStopped = true + console.log('Stopped BrowserStackLocal') + resolve() + }); } - } + }) } -const sleep = promisify(setTimeout) module.exports = async () => { await stopBsLocal() } From aaab352bf0c10b4631f40d50bd2909823ecc0854 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 11 Sep 2023 21:23:21 +0300 Subject: [PATCH 20/21] chore: refactor browserstack config --- packages/js/test/e2e/browsers.ts | 40 ++++++++++++++++++--- packages/js/test/e2e/browserstack.config.ts | 27 +++++--------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/packages/js/test/e2e/browsers.ts b/packages/js/test/e2e/browsers.ts index 37c7e4bfc..39e8650a6 100644 --- a/packages/js/test/e2e/browsers.ts +++ b/packages/js/test/e2e/browsers.ts @@ -12,14 +12,30 @@ const browserStackBrowsers = [ // https://www.browserstack.com/docs/automate/playwright/browsers-and-os name: 'browserstack_chrome_83_windows', use: { - connectOptions: { wsEndpoint: getCdpEndpoint('chrome@83:Windows 10','browserstack_chrome_83_windows') }, + connectOptions: { + wsEndpoint: getCdpEndpoint({ + browser: 'chrome', + browser_version: '83', + os: 'Windows', + os_version: '10', + name: 'browserstack_chrome_83_windows' + }) + }, }, }, { // Chrome latest version name: 'browserstack_chrome_latest_windows', use: { - connectOptions: { wsEndpoint: getCdpEndpoint('chrome@latest:Windows 11','browserstack_chrome_latest_windows') }, + connectOptions: { + wsEndpoint: getCdpEndpoint({ + browser: 'chrome', + browser_version: 'latest', + os: 'Windows', + os_version: '11', + name: 'browserstack_chrome_latest_windows' + }) + }, }, }, { @@ -28,14 +44,30 @@ const browserStackBrowsers = [ // https://www.browserstack.com/docs/automate/playwright/browsers-and-os name: 'browserstack_edge_83_windows', use: { - connectOptions: { wsEndpoint: getCdpEndpoint('edge@83:Windows 10','browserstack_edge_83_windows') }, + connectOptions: { + wsEndpoint: getCdpEndpoint({ + browser: 'edge', + browser_version: '83', + os: 'Windows', + os_version: '10', + name: 'browserstack_edge_83_windows' + }) + }, }, }, { // Edge latest version name: 'browserstack_edge_latest_windows', use: { - connectOptions: { wsEndpoint: getCdpEndpoint('edge@latest:Windows 11','browserstack_edge_latest_windows') }, + connectOptions: { + wsEndpoint: getCdpEndpoint({ + browser: 'edge', + browser_version: 'latest', + os: 'Windows', + os_version: '11', + name: 'browserstack_edge_latest_windows' + }) + }, }, }, ] diff --git a/packages/js/test/e2e/browserstack.config.ts b/packages/js/test/e2e/browserstack.config.ts index ab07d2729..16398db03 100644 --- a/packages/js/test/e2e/browserstack.config.ts +++ b/packages/js/test/e2e/browserstack.config.ts @@ -1,3 +1,6 @@ +// This file is a modified version of the file from the Playwright BrowserStack Typescript example: +// https://github.com/browserstack/typescript-playwright-browserstack/blob/main/browserstack.config.ts#L41C8-L41C8 + import * as cp from 'child_process' import * as BrowserStackLocal from 'browserstack-local' @@ -14,7 +17,7 @@ export const BS_LOCAL_ARGS = { } // BrowserStack Specific Capabilities. -const caps = { +const defaultCapabilities = { browser: 'chrome', browser_version: 'latest', os: 'osx', @@ -30,23 +33,11 @@ const caps = { 'client.playwrightVersion': clientPlaywrightVersion, } -// Patching the capabilities dynamically according to the project name. -const patchCaps = (name: string, title: string) => { - const combination = name.split(/@browserstack/)[0] - const [browserCaps, osCaps] = combination.split(/:/) - const [browser, browser_version] = browserCaps.split(/@/) - const osCapsSplit = osCaps.split(/ /) - const os = osCapsSplit.shift() - const os_version = osCapsSplit.join(' ') - caps.browser = browser ? browser : 'chrome' - caps.browser_version = browser_version ? browser_version : 'latest' - caps.os = os ? os : 'osx' - caps.os_version = os_version ? os_version : 'catalina' - caps.name = title -} +type OverridableCapabilities = + Pick -export function getCdpEndpoint(name: string, title: string){ - patchCaps(name, title) +export function getCdpEndpoint(capabilities: OverridableCapabilities){ + const merged = { ...defaultCapabilities, ...capabilities } - return `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}` + return `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(merged))}` } From 17fea36392208838a4d685f7b856152ffc551f22 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 11 Sep 2023 21:27:21 +0300 Subject: [PATCH 21/21] chore: fix ts --- packages/js/test/e2e/global-teardown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/test/e2e/global-teardown.ts b/packages/js/test/e2e/global-teardown.ts index b607de50f..d444f887e 100644 --- a/packages/js/test/e2e/global-teardown.ts +++ b/packages/js/test/e2e/global-teardown.ts @@ -5,7 +5,7 @@ import { bsLocal } from './browserstack.config' let bsLocalStopped = false const stopBsLocal = async () => { - return new Promise(resolve => { + return new Promise(resolve => { if (bsLocalStopped) { return resolve() }