diff --git a/.c8.json b/.c8.json new file mode 100644 index 00000000..899ac069 --- /dev/null +++ b/.c8.json @@ -0,0 +1,10 @@ +{ + "reporter": ["html"], + "reports-dir": "./c8-cov", + "all": true, + "include": [ + "packages/plugin-selenium-driver/*/plugin/index.*", + "packages/web-application/*/web-client.*", + "packages/web-application/*/web-application.*" + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f415bc24..3f17c6d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ packages/**/dist +packages/**/chrome-cache packages/**/node_modules core/**/dist diff --git a/core/api/tsconfig.json b/core/api/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/api/tsconfig.json +++ b/core/api/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/async-assert/tsconfig.json b/core/async-assert/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/async-assert/tsconfig.json +++ b/core/async-assert/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/async-breakpoints/tsconfig.json b/core/async-breakpoints/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/async-breakpoints/tsconfig.json +++ b/core/async-breakpoints/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/child-process/src/fork.ts b/core/child-process/src/fork.ts index 61745a76..075337b8 100644 --- a/core/child-process/src/fork.ts +++ b/core/child-process/src/fork.ts @@ -21,7 +21,7 @@ const PREFERRED_DEBUG_PORTS: Array = [ const IS_WIN = process.platform === 'win32'; const EMPTY_PARAMETERS = []; const REQUIRE_TS_NODE = ['-r', 'ts-node/register']; -const Module = require("module").Module; +const Module = require('module').Module; const DEFAULT_FORK_OPTIONS: IChildProcessForkOptions = { debug: false, diff --git a/core/child-process/src/index.ts b/core/child-process/src/index.ts index 52f20651..777de0f8 100644 --- a/core/child-process/src/index.ts +++ b/core/child-process/src/index.ts @@ -1,3 +1,4 @@ export {spawn} from './spawn'; +export {spawnWithPipes} from './spawn-with-pipes'; export {fork} from './fork'; export {isChildProcess} from './utils'; diff --git a/core/child-process/src/spawn-with-pipes.ts b/core/child-process/src/spawn-with-pipes.ts new file mode 100644 index 00000000..e77d6283 --- /dev/null +++ b/core/child-process/src/spawn-with-pipes.ts @@ -0,0 +1,18 @@ +import * as childProcess from 'child_process'; +import * as process from 'process'; + +export function spawnWithPipes( + command: string, + args: Array = [], +): childProcess.ChildProcess { + const child = childProcess.spawn(command, args, { + stdio: ['pipe', 'pipe', 'pipe'], // Use pipes for proper control + cwd: process.cwd(), + detached: false, // Run attached to prevent orphan processes + }); + + // Ensure child does not keep the event loop active + child.unref(); + + return child; +} diff --git a/core/child-process/tsconfig.json b/core/child-process/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/child-process/tsconfig.json +++ b/core/child-process/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/cli-config/tsconfig.json b/core/cli-config/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/cli-config/tsconfig.json +++ b/core/cli-config/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/cli/src/commands/runCommand.ts b/core/cli/src/commands/runCommand.ts index c667191e..0a4f7215 100644 --- a/core/cli/src/commands/runCommand.ts +++ b/core/cli/src/commands/runCommand.ts @@ -59,7 +59,6 @@ class RunCommand implements ICLICommand { } async execute() { - const testWorker = new TestWorker(this.transport, { waitForRelease: false, localWorker: this.config.workerLimit === 'local', diff --git a/core/cli/tsconfig.json b/core/cli/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/cli/tsconfig.json +++ b/core/cli/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/dependencies-builder/src/index.ts b/core/dependencies-builder/src/index.ts index 7f667c33..3c48adfc 100644 --- a/core/dependencies-builder/src/index.ts +++ b/core/dependencies-builder/src/index.ts @@ -10,6 +10,7 @@ import { DependencyFileReader, } from '@testring/types'; import {resolveAbsolutePath} from './absolute-path-resolver'; +import * as path from 'node:path'; function getDependencies(absolutePath: string, content: string): Array { const requests: Array = []; @@ -21,14 +22,14 @@ function getDependencies(absolutePath: string, content: string): Array { }); traverse(sourceAST, { - CallExpression(path: NodePath) { - const callee = path.get('callee') as NodePath; + CallExpression(nodePath: NodePath) { + const callee = nodePath.get('callee') as NodePath; if (callee.node.name !== 'require') { return; } - const args = path.get('arguments'); + const args = nodePath.get('arguments'); const firstArgument = args[0]; const dependencyPath: NodePath = firstArgument.get( 'value', @@ -42,24 +43,24 @@ function getDependencies(absolutePath: string, content: string): Array { } function createTreeNode( - path: string, + nodePath: string, content: string, nodes: IDependencyDictionary | null, ): IDependencyTreeNode { return { content, - path, + path: nodePath, nodes, }; } function createDictionaryNode( - path: string, + nodePath: string, content: string, ): IDependencyDictionaryNode { return { content, - path, + path: nodePath, }; } @@ -103,7 +104,7 @@ async function buildNodes( dependencyAbsolutePath.includes('node_modules') || // Fix for local e2e tests running (lerna makes symlink and resolver eats it as path for real file) // require 'node_modules/testring' = require 'packages/testring/dist' - dependencyAbsolutePath.includes('testring/dist') + dependencyAbsolutePath.includes(path.join('testring', 'dist')) ) { continue; } diff --git a/core/dependencies-builder/tsconfig.json b/core/dependencies-builder/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/dependencies-builder/tsconfig.json +++ b/core/dependencies-builder/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/fs-reader/src/file-locator.ts b/core/fs-reader/src/file-locator.ts index b49dee90..c7e7d21e 100644 --- a/core/fs-reader/src/file-locator.ts +++ b/core/fs-reader/src/file-locator.ts @@ -2,11 +2,11 @@ import * as fg from 'fast-glob'; import * as process from 'node:process'; export async function locateFiles(searchpath: string): Promise { - if(!searchpath) { + if (!searchpath) { return []; } if (process.platform === 'win32') { searchpath = fg.convertPathToPattern(searchpath); } return await fg(searchpath, {}); -} \ No newline at end of file +} diff --git a/core/fs-reader/src/file-resolver.ts b/core/fs-reader/src/file-resolver.ts index a4832003..07fe970f 100644 --- a/core/fs-reader/src/file-resolver.ts +++ b/core/fs-reader/src/file-resolver.ts @@ -37,7 +37,7 @@ export async function resolveFiles(files: Array): Promise { // Limit concurrent file reads const readFilePromises = files.map((file) => - limit(() => readFile(file).catch(() => null)) + limit(() => readFile(file).catch(() => null)), ); const filesContent = await Promise.all(readFilePromises); @@ -49,4 +49,3 @@ export async function resolveFiles(files: Array): Promise { return compacted; } - diff --git a/core/fs-reader/test/performance.spec.ts b/core/fs-reader/test/performance.spec.ts index eb94fa0b..c08e16cf 100644 --- a/core/fs-reader/test/performance.spec.ts +++ b/core/fs-reader/test/performance.spec.ts @@ -8,7 +8,9 @@ import {FSReader} from '../src/fs-reader'; import * as process from 'node:process'; import * as fs from 'node:fs'; -const runPerformanceTests = process.env.PERFORMANCE_TESTS === 'true' || process.argv.includes('--performance'); +const runPerformanceTests = + process.env.PERFORMANCE_TESTS === 'true' || + process.argv.includes('--performance'); const glob = path.resolve(__dirname, './fixtures/testfiles/**/**/*.test.js'); const writeTestFiles = async (count: number) => { @@ -28,11 +30,10 @@ const removeTestFiles = async () => { const dir = path.resolve(__dirname, './fixtures/testfiles/performance'); if (fs.existsSync(dir)) { - await fs.promises.rm(dir, { recursive: true, force: true }); + await fs.promises.rm(dir, {recursive: true, force: true}); } }; - describe('Performance', function () { this.timeout(120000); if (!runPerformanceTests) { @@ -41,7 +42,6 @@ describe('Performance', function () { }); } else { describe('FSReader', () => { - before(async () => { await writeTestFiles(15000); }); diff --git a/core/fs-reader/tsconfig.json b/core/fs-reader/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/fs-reader/tsconfig.json +++ b/core/fs-reader/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/fs-store/src/fs-store-client.ts b/core/fs-store/src/fs-store-client.ts index 917a4e83..a4583047 100644 --- a/core/fs-store/src/fs-store-client.ts +++ b/core/fs-store/src/fs-store-client.ts @@ -68,9 +68,8 @@ export class FSStoreClient { delete this.reqHash[requestId]; } else { this.reqHash[requestId].fullPath = fullPath; - this.reqHash[requestId].meta.fileName = path.basename( - fullPath, - ); + this.reqHash[requestId].meta.fileName = + path.basename(fullPath); } } diff --git a/core/fs-store/src/fs-store-server.ts b/core/fs-store/src/fs-store-server.ts index f41bb8bf..96438765 100644 --- a/core/fs-store/src/fs-store-server.ts +++ b/core/fs-store/src/fs-store-server.ts @@ -57,10 +57,8 @@ export class FSStoreServer extends PluggableModule { private unHookCleanWorkerTransport: (() => void) | null = null; private defaultFsPermisionPool: LockPool; - private files: Record< - string, - [FilePermissionResolver, cleanUpCBRecord] - > = {}; + private files: Record = + {}; private inWorkRequests: Record< string, Record diff --git a/core/fs-store/tsconfig.json b/core/fs-store/tsconfig.json index b9e214f3..ba11ef14 100644 --- a/core/fs-store/tsconfig.json +++ b/core/fs-store/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/logger/tsconfig.json b/core/logger/tsconfig.json index 3bc9315c..b6ad567c 100644 --- a/core/logger/tsconfig.json +++ b/core/logger/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/pluggable-module/tsconfig.json b/core/pluggable-module/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/pluggable-module/tsconfig.json +++ b/core/pluggable-module/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/plugin-api/tsconfig.json b/core/plugin-api/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/plugin-api/tsconfig.json +++ b/core/plugin-api/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/sandbox/test/sandbox.spec.ts b/core/sandbox/test/sandbox.spec.ts index 314d14a0..f3973a83 100644 --- a/core/sandbox/test/sandbox.spec.ts +++ b/core/sandbox/test/sandbox.spec.ts @@ -80,16 +80,8 @@ describe('Sandbox', () => { it('should correctly pass "instanceof" check for all primitives', async () => { const source = await fixturesFileReader('primitives.js'); const sandbox = new Sandbox(source, 'primitives.js', {}); - const { - array, - map, - set, - weakMap, - weakSet, - promise, - buffer, - error, - } = sandbox.execute(); + const {array, map, set, weakMap, weakSet, promise, buffer, error} = + sandbox.execute(); chai.expect(array instanceof Array).to.be.equal(true); chai.expect(map instanceof Map).to.be.equal(true); diff --git a/core/sandbox/tsconfig.json b/core/sandbox/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/sandbox/tsconfig.json +++ b/core/sandbox/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/test-run-controller/src/test-run-controller.ts b/core/test-run-controller/src/test-run-controller.ts index 1ba3d2ff..3592833a 100644 --- a/core/test-run-controller/src/test-run-controller.ts +++ b/core/test-run-controller/src/test-run-controller.ts @@ -23,7 +23,8 @@ const delay = (milliseconds: number) => export class TestRunController extends PluggableModule - implements ITestRunController { + implements ITestRunController +{ private workers: Array = []; private errors: Array = []; @@ -189,13 +190,8 @@ export class TestRunController private getQueueItemWithRunData(queueItem): IQueuedTest { let screenshotsEnabled = false; const isRetryRun = queueItem.retryCount > 0; - const { - debug, - httpThrottle, - logLevel, - devtool, - screenshotPath, - } = this.config; + const {debug, httpThrottle, logLevel, devtool, screenshotPath} = + this.config; if (this.config.screenshots === 'enable') { screenshotsEnabled = true; diff --git a/core/test-run-controller/tsconfig.json b/core/test-run-controller/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/test-run-controller/tsconfig.json +++ b/core/test-run-controller/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/test-worker/src/test-worker-instance.ts b/core/test-worker/src/test-worker-instance.ts index 6a156217..cc20e711 100644 --- a/core/test-worker/src/test-worker-instance.ts +++ b/core/test-worker/src/test-worker-instance.ts @@ -260,11 +260,12 @@ export class TestWorkerInstance implements ITestWorkerInstance { completeHandler, ); } else { - removeListener = this.transport.onceFrom( - this.getWorkerID(), - TestWorkerAction.executionComplete, - completeHandler, - ); + removeListener = + this.transport.onceFrom( + this.getWorkerID(), + TestWorkerAction.executionComplete, + completeHandler, + ); } this.successTestExecution = () => { diff --git a/core/test-worker/tsconfig.json b/core/test-worker/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/test-worker/tsconfig.json +++ b/core/test-worker/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/testring/tsconfig.json b/core/testring/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/testring/tsconfig.json +++ b/core/testring/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/transport/tsconfig.json b/core/transport/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/transport/tsconfig.json +++ b/core/transport/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/types/src/async-assert/index.ts b/core/types/src/async-assert/index.ts index 19bd270a..9edc19c9 100644 --- a/core/types/src/async-assert/index.ts +++ b/core/types/src/async-assert/index.ts @@ -1,4 +1,4 @@ -import type { use as chaiUse } from 'chai'; +import type {use as chaiUse} from 'chai'; type First = T extends [infer A, ...any[]] ? A : never; type ChaiPlugin = First>; diff --git a/core/types/src/browser-proxy/enums.ts b/core/types/src/browser-proxy/enums.ts index 229642ec..76df464b 100644 --- a/core/types/src/browser-proxy/enums.ts +++ b/core/types/src/browser-proxy/enums.ts @@ -13,7 +13,6 @@ export const enum BrowserProxyActions { click = 'click', execute = 'execute', executeAsync = 'executeAsync', - gridProxyDetails = 'gridProxyDetails', url = 'url', newWindow = 'newWindow', waitForExist = 'waitForExist', @@ -21,7 +20,7 @@ export const enum BrowserProxyActions { isVisible = 'isVisible', moveToObject = 'moveToObject', getTitle = 'getTitle', - clearElement = 'clearElement', + clearValue = 'clearValue', keys = 'keys', elementIdText = 'elementIdText', elements = 'elements', @@ -57,7 +56,6 @@ export const enum BrowserProxyActions { isSelected = 'isSelected', getText = 'getText', elementIdSelected = 'elementIdSelected', - timeoutsAsyncScript = 'timeoutsAsyncScript', makeScreenshot = 'makeScreenshot', uploadFile = 'uploadFile', end = 'end', @@ -69,11 +67,27 @@ export const enum BrowserProxyActions { waitForSelected = 'waitForSelected', waitUntil = 'waitUntil', selectByAttribute = 'selectByAttribute', - getGridNodeDetails = 'getGridNodeDetails', gridTestSession = 'gridTestSession', keysOnElement = 'keysOnElement', mock = 'mock', getMockData = 'getMockData', getCdpCoverageFile = 'getCdpCoverageFile', emulateDevice = 'emulateDevice', + getHubConfig = 'getHubConfig', + status = 'status', + back = 'back', + forward = 'forward', + getActiveElement = 'getActiveElement', + getLocation = 'getLocation', + setTimeZone = 'setTimeZone', + getWindowSize = 'getWindowSize', + savePDF = 'savePDF', + addValue = 'addValue', + doubleClick = 'doubleClick', + isClickable = 'isClickable', + waitForClickable = 'waitForClickable', + isFocused = 'isFocused', + isStable = 'isStable', + waitForEnabled = 'waitForEnabled', + waitForStable = 'waitForStable', } diff --git a/core/types/src/browser-proxy/index.ts b/core/types/src/browser-proxy/index.ts index cd9516bd..f860a30f 100644 --- a/core/types/src/browser-proxy/index.ts +++ b/core/types/src/browser-proxy/index.ts @@ -26,8 +26,6 @@ export interface IBrowserProxyPlugin { click(applicant: string, selector: string, options?: any): Promise; - gridProxyDetails(applicant: string): Promise; - url(applicant: string, val: string): Promise; newWindow( @@ -68,7 +66,7 @@ export interface IBrowserProxyPlugin { getTitle(applicant: string): Promise; - clearElement(applicant: string, xpath: string): Promise; + clearValue(applicant: string, xpath: string): Promise; keys(applicant: string, value: any): Promise; @@ -196,7 +194,7 @@ export interface IBrowserProxyPlugin { value: string, ): Promise; - getGridNodeDetails(applicant: string): Promise; - gridTestSession(applicant: string): Promise; + + getHubConfig(applicant: string): Promise; } diff --git a/core/types/src/plugin.ts b/core/types/src/plugin.ts index a8042785..8b8b24df 100644 --- a/core/types/src/plugin.ts +++ b/core/types/src/plugin.ts @@ -5,9 +5,7 @@ import {ITestRunController} from './test-run-controller'; import {IPluggableModule} from './pluggable-module'; import {IBrowserProxyController} from './browser-proxy'; import {IHttpClient} from './http-api'; -import { - IHttpServerController, -} from './devtool-backend'; +import {IHttpServerController} from './devtool-backend'; export type PluginConfig = object | null; diff --git a/core/types/src/web-application/index.ts b/core/types/src/web-application/index.ts index c15847c2..0e334aa3 100644 --- a/core/types/src/web-application/index.ts +++ b/core/types/src/web-application/index.ts @@ -51,3 +51,18 @@ export interface IWindowFeatures { } export type WebApplicationDevtoolCallback = (err: null | Error) => void; + +export type SavePdfOptions = { + filepath: string; + orientation: string; + scale: number; + background: boolean; + width: number; + height: number; + top: number; + bottom: number; + left: number; + right: number; + shrinkToFit: boolean; + pageRanges: Array; +}; diff --git a/core/types/tsconfig.json b/core/types/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/types/tsconfig.json +++ b/core/types/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/core/utils/src/plugin-require.ts b/core/utils/src/plugin-require.ts index 68a2660c..40a670eb 100644 --- a/core/utils/src/plugin-require.ts +++ b/core/utils/src/plugin-require.ts @@ -20,11 +20,14 @@ function normalizeExport(module) { export function requirePlugin(pluginPath: string): any { let resolvedPlugin; - const parentModule = path.join(__dirname, '../..') + const parentModule = path.join(__dirname, '../..'); for (let index = 0; index < PREFIXES.length; index++) { try { - resolvedPlugin = resolvePackage(PREFIXES[index] + pluginPath, parentModule); + resolvedPlugin = resolvePackage( + PREFIXES[index] + pluginPath, + parentModule, + ); } catch (e) { continue; } diff --git a/core/utils/test/find-available-ports.spec.ts b/core/utils/test/find-available-ports.spec.ts index 954e62e1..391a6beb 100644 --- a/core/utils/test/find-available-ports.spec.ts +++ b/core/utils/test/find-available-ports.spec.ts @@ -109,10 +109,11 @@ describe('find ports', () => { chai.expect(await isAvailablePort(45403, DEFAULT_HOST)).to.be.equal( true, ); - const port = await getAvailableFollowingPort(45400, DEFAULT_HOST, [ - 45401, - 45402, - ]); + const port = await getAvailableFollowingPort( + 45400, + DEFAULT_HOST, + [45401, 45402], + ); chai.expect(port).to.be.equal(45403); chai.expect(await isAvailablePort(port, DEFAULT_HOST)).to.equal(true); diff --git a/core/utils/tsconfig.json b/core/utils/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/core/utils/tsconfig.json +++ b/core/utils/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/package-lock.json b/package-lock.json index 0f54efc1..c87feb5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -348,30 +348,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -396,9 +396,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.5.tgz", - "integrity": "sha512-Kkm8C8uxI842AwQADxl0GbcG1rupELYLShazYEZO/2DYjhyWXJIOUVOE3tBYm6JXzUCNJOZEzqc4rCW/jsEQYQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.8.tgz", + "integrity": "sha512-3tBctaHRW6xSub26z7n8uyOTwwUsCdvIug/oxBH9n6yCO5hMj2vwDJAo7RbBMKrM7P+W2j61zLKviJQFGOYKMg==", "dev": true, "license": "MIT", "dependencies": { @@ -435,13 +435,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -489,18 +489,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "engines": { @@ -737,25 +737,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -958,11 +958,18 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1089,6 +1096,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", @@ -1139,15 +1162,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -1420,13 +1443,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1536,14 +1559,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", - "dev": true, + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1908,14 +1931,14 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", - "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.9.tgz", + "integrity": "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -1988,13 +2011,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2004,13 +2027,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2020,9 +2043,9 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.5.tgz", - "integrity": "sha512-GJhPO0y8SD5EYVCy2Zr+9dSZcEgaSmq5BLR0Oc25TOEhC+ba49vUAGZFjy8v79z9E1mdldq4x9d1xgh4L1d5dQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", + "integrity": "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==", "dev": true, "license": "MIT", "dependencies": { @@ -2107,15 +2130,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -2127,9 +2150,9 @@ "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -2140,21 +2163,21 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -2170,17 +2193,17 @@ "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -2190,6 +2213,50 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2257,9 +2324,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "dev": true, "license": "MIT", "dependencies": { @@ -2270,30 +2337,30 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", - "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.5", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.5", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2311,9 +2378,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2323,6 +2390,15 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -2356,9 +2432,9 @@ } }, "node_modules/@electron/asar": { - "version": "3.2.18", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", - "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.3.1.tgz", + "integrity": "sha512-WtpC/+34p0skWZiarRjLAyqaAX78DofhDxnREy/V5XHfu1XEXbFCSSMcDQ6hNCPJFaPy8/NnUgYuf9uiCkvKPg==", "license": "MIT", "dependencies": { "commander": "^5.0.0", @@ -2694,7 +2770,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4336,9 +4411,9 @@ } }, "node_modules/@nx/devkit": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.3.2.tgz", - "integrity": "sha512-VhbxEsSTCZlOVgjuQC+6HQmb9Oz9VoHUeo4001Pw6BFBcSXZUi5q37C/lxbAgQPnMKLkFcLva3WKZ+fOLwhGIg==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.4.4.tgz", + "integrity": "sha512-gwTsgHIIVKjLDPAC32/cWMJRMabT7g64guyQdu9Rp+xbIJfSI+NSYVGftMHljxY1eWbYpN392y9UEVkYjQfzvg==", "dev": true, "license": "MIT", "dependencies": { @@ -4382,9 +4457,9 @@ } }, "node_modules/@nx/nx-darwin-arm64": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.3.2.tgz", - "integrity": "sha512-lQOXMIPmE9o36TuZ+SX6iq7PPWa3s1fjNRqCujlviExX69245NNCMxd754gXlLrsxC1onrx/zmJciKmmEWDIiw==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.4.4.tgz", + "integrity": "sha512-dlNrC7yYGVOeS6YZLJfRZLioZQF6aAPNYHHBexU1XnJq/1QS1pJEKdW582KsQGw+ZQqWiIr1XmexIjewMpx0Xg==", "cpu": [ "arm64" ], @@ -4399,9 +4474,9 @@ } }, "node_modules/@nx/nx-darwin-x64": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.3.2.tgz", - "integrity": "sha512-RvvSz4QYVOYOfC8sUE63b6dy8iHk2AEI0r1FF5FCQuqE1DdTeTjPETY2sY35tRqF+mO/6oLGp2+m9ti/ysRoTg==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.4.4.tgz", + "integrity": "sha512-CLQ5mjAmjCnKuTGybaVBYQo+Me9ZXRiWXZOm8Vu7hbUtPgKob0ldnk0pIy8wqlNnfBV+YHPQ0lpHUUQ80iG8IQ==", "cpu": [ "x64" ], @@ -4416,9 +4491,9 @@ } }, "node_modules/@nx/nx-freebsd-x64": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.3.2.tgz", - "integrity": "sha512-KBDTyGn1evlZ17pupwRUDh2wrCMuHhP2j8cOCdgF5cl7vRki8BOK9yyL6jD11d/d/6DgXzy1jmQEX4Xx+AGCug==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.4.4.tgz", + "integrity": "sha512-coIZJq/fCkSxzVS/i9HQzPSPVPiONFlJ2Rw/OsGbNB/PD+3vGktYPnoFg7l8QxiH9b2hFuHUKK8TXBBd16z/Nw==", "cpu": [ "x64" ], @@ -4433,9 +4508,9 @@ } }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.3.2.tgz", - "integrity": "sha512-mW+OcOnJEMvs7zD3aSwEG3z5M9bI4CuUU5Q/ePmnNzWIucRHpoAMNt/Sd+yu6L4+QttvoUf967uwcMsX8l4nrw==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.4.4.tgz", + "integrity": "sha512-YKY9WOn66AyQcNV4QrZIfHu67ip1+BTblRVRUF4ekMzOxHzmHbuyIqdF0GuDy5a8etFi2cKqZ+ZD5Rrr6xY78w==", "cpu": [ "arm" ], @@ -4450,9 +4525,9 @@ } }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.3.2.tgz", - "integrity": "sha512-hbXpZqUvGY5aeEWvh0SNsiYjP1ytSM30XOT6qN6faLO2CL/7j9D2UB69SKOqF3TJOvuNU6cweFgZCxyGfXBYIQ==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.4.4.tgz", + "integrity": "sha512-HbZyjKQVm4T0FX2rjFedLqCcdLx3JjQmYDNLga/hG3f5CnhMWb2z35PWJbPVuCN/vC3r8FZeqqyB5csx8/hu5w==", "cpu": [ "arm64" ], @@ -4467,9 +4542,9 @@ } }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.3.2.tgz", - "integrity": "sha512-HXthtN7adXCNVWs2F4wIqq2f7BcKTjsEnqg2LWV5lm4hRYvMfEvPftb0tECsEhcSQQYcvIJnLfv3vtu9HZSfVA==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.4.4.tgz", + "integrity": "sha512-7rrvV85kM4FCc9ui3hfG7dc3leUxVTZSjN4QaaAqHG3vMJFey52Ao/C82GaO73e6C+zQjN6rhxGMwx/m3BQwpA==", "cpu": [ "arm64" ], @@ -4484,9 +4559,9 @@ } }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.3.2.tgz", - "integrity": "sha512-HhgHqOUT05H45zuQL+XPywQbRNFttd7Rkkr7dZnpCRdp4W8GDjfyKCoCS5qVyowAyNh9Vc7VEq9qmiLMlvf6Zg==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.4.4.tgz", + "integrity": "sha512-ZMtRbzdwjt3e9snnUa8sTyNY3vZlVtU4gQLb9CC9re23j1ZdUrJsqPVHlCQWCwpbZ8UN67ptCe40Wr590OHA1w==", "cpu": [ "x64" ], @@ -4501,9 +4576,9 @@ } }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.3.2.tgz", - "integrity": "sha512-NrZ8L9of2GmYEM8GMJX6QRrLJlAwM+ds2rhdY1bxwpiyCNcD3IO/gzJlBs+kG4ly05F1u/X4k/FI5dXPpjUSgw==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.4.4.tgz", + "integrity": "sha512-Ff8lJLrsJgfywp7cmr+ERHJ1pesEortJx4s0P5GugSioqqQx0pNi40YCWKRUKy5aZ1+HCysSAjGLtxmx+fSv+g==", "cpu": [ "x64" ], @@ -4518,9 +4593,9 @@ } }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.3.2.tgz", - "integrity": "sha512-yLjacZND7C1XmsC0jfRLSgeLWZUw2Oz+u3nXNvj5JX6YHtYTVLFnRbTAcI+pG2Y6v0Otf2GKb3VT5d1mQb8JvA==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.4.4.tgz", + "integrity": "sha512-ayJ4tOyr2YjlYNFpbYUeSVAksupQea82bTuB9q4Scvzh35PU3UvMF9TYNt3ficBv2jedW/yD6dzHBbZJDHS1/A==", "cpu": [ "arm64" ], @@ -4535,9 +4610,9 @@ } }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.3.2.tgz", - "integrity": "sha512-oDhcctfk0UB1V+Otp1161VKNMobzkFQxGyiEIjp0CjCBa2eRHC1r35L695F1Hj0bvLQPSni9XIe9evh2taeAkg==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.4.4.tgz", + "integrity": "sha512-c4z4eRmkGGgH9WCFEI8wK1eyXyk2rREhhjuuEmxeJYBQB/SiWjRDBIUyIiJe2ItsWJdEHPKdPQJL52xgTICSVA==", "cpu": [ "x64" ], @@ -4773,19 +4848,19 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", + "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", "license": "Apache-2.0", "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.4.0", - "semver": "7.6.0", - "tar-fs": "3.0.5", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" @@ -4794,55 +4869,16 @@ "node": ">=18" } }, - "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/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==", - "license": "MIT" - }, - "node_modules/@puppeteer/browsers/node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 14" + "node": ">=10" } }, "node_modules/@rtsao/scc": { @@ -5474,6 +5510,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/nanoid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-3.0.0.tgz", @@ -5569,14 +5614,15 @@ } }, "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", - "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", + "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, "engines": { @@ -5922,9 +5968,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, "license": "ISC" }, @@ -6112,9 +6158,9 @@ } }, "node_modules/@wdio/repl/node_modules/@types/node": { - "version": "20.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", - "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -6133,9 +6179,9 @@ } }, "node_modules/@wdio/types/node_modules/@types/node": { - "version": "20.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", - "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -6165,63 +6211,6 @@ "node": ">=18.20.0" } }, - "node_modules/@wdio/utils/node_modules/@puppeteer/browsers": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", - "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.0", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@wdio/utils/node_modules/bare-fs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", - "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^3.0.0", - "bare-stream": "^2.0.0" - }, - "engines": { - "bare": ">=1.7.0" - } - }, - "node_modules/@wdio/utils/node_modules/bare-os": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", - "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.6.0" - } - }, - "node_modules/@wdio/utils/node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, "node_modules/@wdio/utils/node_modules/decamelize": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", @@ -6246,18 +6235,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@wdio/utils/node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -6267,31 +6244,6 @@ "node": ">= 10.x" } }, - "node_modules/@wdio/utils/node_modules/tar-fs": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", - "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/@wdio/utils/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -6567,9 +6519,9 @@ "license": "BSD-3-Clause" }, "node_modules/@zip.js/zip.js": { - "version": "2.7.54", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", - "integrity": "sha512-qMrJVg2hoEsZJjMJez9yI2+nZlBUxgYzGV3mqcb2B/6T1ihXp0fWBDYlVHlHquuorgNUQP5a8qSmX6HF5rFJNg==", + "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", "license": "BSD-3-Clause", "engines": { "bun": ">=0.7.0", @@ -6853,6 +6805,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -7342,6 +7300,16 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -8049,9 +8017,9 @@ } }, "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.1.0.tgz", + "integrity": "sha512-f9B1xMdnkCIqe+2dHrJsoQFRz7reChaAHE/65SdaykPklQqhme2WaC08oD3is77x9ff98/9EazAKFDZv5rFEQg==", "dev": true, "license": "MIT", "dependencies": { @@ -8062,6 +8030,7 @@ "@babel/plugin-proposal-numeric-separator": "^7.16.0", "@babel/plugin-proposal-optional-chaining": "^7.16.0", "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", "@babel/plugin-transform-flow-strip-types": "^7.16.0", "@babel/plugin-transform-react-display-name": "^7.16.0", "@babel/plugin-transform-runtime": "^7.16.4", @@ -8178,38 +8147,44 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", - "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", + "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", - "bare-path": "^2.0.0", + "bare-path": "^3.0.0", "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.7.0" } }, "node_modules/bare-os": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", - "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", + "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", "license": "Apache-2.0", - "optional": true + "optional": true, + "engines": { + "bare": ">=1.6.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.4.tgz", - "integrity": "sha512-G6i3A74FjNq4nVrrSTUz5h3vgXzBJnjmWAVlBWaZETkgu+LgKd7AiyOml3EDJY1AHlIbBHKDXE+TUT53Ff8OaA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -8473,7 +8448,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/buildcheck": { @@ -8492,6 +8466,17 @@ "dev": true, "license": "MIT" }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/byte-size": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", @@ -8511,6 +8496,148 @@ "node": ">= 0.8" } }, + "node_modules/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/c8/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/c8/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/c8/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/c8/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/c8/node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/cacache": { "version": "18.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", @@ -8704,9 +8831,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8771,9 +8898,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "funding": [ { "type": "opencollective", @@ -8965,9 +9092,9 @@ } }, "node_modules/chromedriver": { - "version": "132.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-132.0.0.tgz", - "integrity": "sha512-jECVJjty5ypYKptQ/QCf8Q0Iq0qq2eW5x1WnYwlGCgmBBcwDH+XrdjFKc4mA3lFO1p3dpOUgTbayKCiGpMPBjg==", + "version": "133.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-133.0.1.tgz", + "integrity": "sha512-Z9VrJ9547daetazzP4k+7COj0PTtkomvtt8OZr8swTzsqDOzG0Xymdxg0dUtptc4da3X9Zts1iZoxOkm7blx/g==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -9680,18 +9807,18 @@ } }, "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", - "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", "dev": true, "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" + "unicorn-magic": "^0.3.0" }, "engines": { "node": ">=18" @@ -9700,14 +9827,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/copy-webpack-plugin/node_modules/ignore": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/copy-webpack-plugin/node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10196,9 +10333,9 @@ } }, "node_modules/deepmerge-ts": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.3.tgz", - "integrity": "sha512-qCSH6I0INPxd9Y1VtAiLpnYvz5O//6rCfJXKk0z66Up9/VOSr+1yS8XSKA5IWRxjocFGlzPyaZYe+jxq7OOLtQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.4.tgz", + "integrity": "sha512-fxqo6nHGQ9zOVgI4KXqtWXJR/yCLtC7aXIVq+6jc8tHPFUxlFmuUcm2kC4vztQ+LJxQ3gER/XAWearGYQ8niGA==", "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -10715,9 +10852,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.84", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz", - "integrity": "sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g==", + "version": "1.5.101", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", + "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -10803,9 +10940,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -11016,7 +11153,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -11029,13 +11165,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -11839,9 +11978,9 @@ } }, "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", "dev": true, "license": "Apache-2.0" }, @@ -12085,9 +12224,9 @@ } }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -12303,7 +12442,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -12407,13 +12545,19 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -12442,13 +12586,14 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -12748,41 +12893,6 @@ "node": "^16.13 || >=18 || >=20" } }, - "node_modules/geckodriver/node_modules/bare-fs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", - "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^3.0.0", - "bare-stream": "^2.0.0" - }, - "engines": { - "bare": ">=1.7.0" - } - }, - "node_modules/geckodriver/node_modules/bare-os": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", - "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.6.0" - } - }, - "node_modules/geckodriver/node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, "node_modules/geckodriver/node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -12820,40 +12930,15 @@ "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/geckodriver/node_modules/tar-fs": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", - "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/geckodriver/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/geckodriver/node_modules/which": { @@ -13532,7 +13617,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -13780,6 +13864,12 @@ "node": ">= 4" } }, + "node_modules/ignore-loader": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", + "integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==", + "dev": true + }, "node_modules/ignore-walk": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", @@ -13826,9 +13916,9 @@ "license": "MIT" }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14155,12 +14245,13 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -14203,13 +14294,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -14527,9 +14618,9 @@ } }, "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz", + "integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -14648,13 +14739,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -14926,9 +15017,9 @@ } }, "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.3.tgz", + "integrity": "sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -16276,7 +16367,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -16343,6 +16433,7 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", "dev": true, "license": "MIT" }, @@ -17335,6 +17426,87 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/multer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/multer/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/multer/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/multer/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/multimatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", @@ -18028,9 +18200,9 @@ } }, "node_modules/nx": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/nx/-/nx-20.3.2.tgz", - "integrity": "sha512-VWUHX0uCn8ACFbpBTpgucDzwe4q/a/UU3AYOhzKCvTzb3kQiyvoxLjORSze93ZNEqgor0PMkCQgcoMBUjxJfzQ==", + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/nx/-/nx-20.4.4.tgz", + "integrity": "sha512-gOR9YFDbDTnOQYy6oqJ60ExgCiJJnz+o1TxrmP6cRmpRVv+5RmuZWQ9fXPtaj4SA69jbNqhSVyopT8oO6I+Gaw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -18075,16 +18247,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "20.3.2", - "@nx/nx-darwin-x64": "20.3.2", - "@nx/nx-freebsd-x64": "20.3.2", - "@nx/nx-linux-arm-gnueabihf": "20.3.2", - "@nx/nx-linux-arm64-gnu": "20.3.2", - "@nx/nx-linux-arm64-musl": "20.3.2", - "@nx/nx-linux-x64-gnu": "20.3.2", - "@nx/nx-linux-x64-musl": "20.3.2", - "@nx/nx-win32-arm64-msvc": "20.3.2", - "@nx/nx-win32-x64-msvc": "20.3.2" + "@nx/nx-darwin-arm64": "20.4.4", + "@nx/nx-darwin-x64": "20.4.4", + "@nx/nx-freebsd-x64": "20.4.4", + "@nx/nx-linux-arm-gnueabihf": "20.4.4", + "@nx/nx-linux-arm64-gnu": "20.4.4", + "@nx/nx-linux-arm64-musl": "20.4.4", + "@nx/nx-linux-x64-gnu": "20.4.4", + "@nx/nx-linux-x64-musl": "20.4.4", + "@nx/nx-win32-arm64-msvc": "20.4.4", + "@nx/nx-win32-x64-msvc": "20.4.4" }, "peerDependencies": { "@swc-node/register": "^1.8.0", @@ -18504,16 +18676,15 @@ "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, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -18813,7 +18984,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -19113,9 +19283,9 @@ "license": "MIT" }, "node_modules/parse-path": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", - "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.1.tgz", + "integrity": "sha512-6ReLMptznuuOEzLoGEa+I1oWRSj2Zna5jLWC+l6zlfAI4dbbSaIES29ThzuPkbhNahT65dWzfoZEO6cfJw2Ksg==", "dev": true, "license": "MIT", "dependencies": { @@ -19189,7 +19359,6 @@ "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, "license": "MIT", "engines": { "node": ">=8" @@ -19401,9 +19570,9 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -19411,9 +19580,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "dev": true, "funding": [ { @@ -19471,9 +19640,9 @@ } }, "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -19501,9 +19670,9 @@ } }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -19869,9 +20038,9 @@ "license": "MIT" }, "node_modules/protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", + "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", "dev": true, "license": "MIT" }, @@ -19977,6 +20146,57 @@ "node": ">=18" } }, + "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", + "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "license": "Apache-2.0", + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/puppeteer-core/node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/puppeteer-core/node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, "node_modules/puppeteer-core/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -20000,12 +20220,65 @@ "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", "license": "BSD-3-Clause" }, + "node_modules/puppeteer-core/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/puppeteer-core/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==", "license": "MIT" }, + "node_modules/puppeteer-core/node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/puppeteer-core/node_modules/tar-fs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/puppeteer-core/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/puppeteer-core/node_modules/ws": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", @@ -20074,12 +20347,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "license": "MIT" - }, "node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -21679,9 +21946,9 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "license": "MIT", "dependencies": { "ip-address": "^9.0.5", @@ -22141,14 +22408,21 @@ "node": ">= 4.0.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/streamx": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", - "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -22514,17 +22788,17 @@ } }, "node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/tar-fs/node_modules/tar-stream": { @@ -22641,9 +22915,9 @@ } }, "node_modules/terser": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", - "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -23235,7 +23509,6 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true, "license": "MIT" }, "node_modules/typedarray-to-buffer": { @@ -23374,9 +23647,9 @@ } }, "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, "license": "MIT", "engines": { @@ -23761,9 +24034,9 @@ } }, "node_modules/webdriver/node_modules/@types/node": { - "version": "20.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", - "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -23816,9 +24089,9 @@ } }, "node_modules/webdriverio/node_modules/@types/node": { - "version": "20.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", - "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -24826,7 +25099,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4" @@ -25070,6 +25342,7 @@ "@types/webpack": "5.28.5", "chrome-launcher": "1.1.2", "copy-webpack-plugin": "12.0.2", + "ignore-loader": "0.1.2", "os-browserify": "0.3.0", "path-browserify": "1.0.1", "ts-loader": "9.5.1", @@ -25089,6 +25362,7 @@ "@types/react-dom": "18.3.1", "@types/webpack": "5.28.5", "css-loader": "7.1.2", + "ignore-loader": "0.1.2", "monaco-editor-webpack-plugin": "7.1.0", "os-browserify": "0.3.0", "path-browserify": "1.0.1", @@ -25117,15 +25391,19 @@ "version": "0.7.2", "license": "MIT", "dependencies": { + "@puppeteer/browsers": "2.7.0", "@testring/cli": "0.7.2", "@testring/plugin-babel": "0.7.2", "@testring/plugin-fs-store": "0.7.2", "@testring/plugin-selenium-driver": "0.7.2", "@testring/web-application": "0.7.2", "@types/express": "5.0.0", + "@types/multer": "1.4.12", "babel-preset-es2015": "6.24.1", + "c8": "10.1.3", "concurrently": "9.0.1", "express": "4.21.1", + "multer": "1.4.5-lts.1", "testring": "0.7.2", "ts-node": "10.9.2" } @@ -25164,21 +25442,43 @@ "@types/babel__core": "7.20.5" } }, - "packages/plugin-babel/node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "packages/plugin-babel/node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "packages/plugin-babel/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "packages/plugin-fs-store": { diff --git a/package.json b/package.json index e24d1e26..2d87e410 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "check-deps:find-updates": "ncu -m --deep --color", "check-deps:lerna-update": "lernaupdate", "check-deps:lerna-dedupe": "lernaupdate -d", - "githook:precommit": "npm run check-deps:validate && npm run build && npm run lint && npm run test && npm run test:e2e-simple" + "githook:precommit": "npm run check-deps:validate && npm run build && npm run lint && npm run test && npm run test:e2e-simple", + "test:e2e-coverage": "c8 --config .c8.json ts-node ./packages/e2e-test-app/src/test-runner.ts --config ./packages/e2e-test-app/test/selenium/config.coverage.js --env-config=./packages/e2e-test-app/test/selenium/env.json --headless" }, "precommit": "githook:precommit", "nyc": { diff --git a/packages/browser-proxy/src/browser-proxy-controller.ts b/packages/browser-proxy/src/browser-proxy-controller.ts index 22a289ec..28e61fcd 100644 --- a/packages/browser-proxy/src/browser-proxy-controller.ts +++ b/packages/browser-proxy/src/browser-proxy-controller.ts @@ -1,5 +1,4 @@ import {ChildProcess} from 'child_process'; - import { BrowserProxyActions, BrowserProxyPlugins, @@ -11,35 +10,31 @@ import { } from '@testring/types'; import {PluggableModule} from '@testring/pluggable-module'; import {loggerClient} from '@testring/logger'; - import {BrowserProxyWorker} from './browser-proxy-worker'; +import {BrowserProxyLocalWorker} from './browser-proxy-local-worker'; const logger = loggerClient.withPrefix('[browser-proxy-controller]'); export class BrowserProxyController extends PluggableModule - implements IBrowserProxyController { + implements IBrowserProxyController +{ private workersPool: Set = new Set(); - private applicantWorkerMap: Map = new Map(); - private defaultExternalPlugin: IBrowserProxyWorkerConfig = { plugin: 'unknown', config: null, }; - private externalPlugin: IBrowserProxyWorkerConfig; - private lastWorkerIndex = -1; - - private workerLimit = 1; - + private workerLimit: number | 'local' = 1; private logger = logger; + private localWorker: BrowserProxyLocalWorker | null = null; constructor( private transport: ITransport, private workerCreator: ( - onActionPluginPath: string, + pluginPath: string, config: any, ) => ChildProcess | Promise, ) { @@ -60,19 +55,28 @@ export class BrowserProxyController BrowserProxyPlugins.getPlugin, this.defaultExternalPlugin, ); - const {config} = this.externalPlugin; - if ( - config && - typeof config.workerLimit === 'number' && - !isNaN(config.workerLimit) - ) { - this.workerLimit = config.workerLimit; + if (config && config.workerLimit) { + this.workerLimit = + config.workerLimit === 'local' + ? 'local' + : Number(config.workerLimit); + } + + if (this.workerLimit === 'local') { + this.localWorker = new BrowserProxyLocalWorker( + this.transport, + this.externalPlugin, + ); } } private getWorker(applicant: string): IBrowserProxyWorker { + if (this.workerLimit === 'local' && this.localWorker) { + return this.localWorker; + } + const mappedWorker = this.applicantWorkerMap.get(applicant); let worker; @@ -80,7 +84,7 @@ export class BrowserProxyController return mappedWorker; } - if (this.workersPool.size < this.workerLimit) { + if (this.workersPool.size < (this.workerLimit as number)) { worker = new BrowserProxyWorker( this.transport, this.workerCreator, @@ -97,7 +101,6 @@ export class BrowserProxyController } this.applicantWorkerMap.set(applicant, worker); - return worker; } @@ -105,36 +108,30 @@ export class BrowserProxyController applicant: string, command: IBrowserProxyCommand, ): Promise { - let worker; - if (command.action === BrowserProxyActions.end) { - if (this.applicantWorkerMap.has(applicant)) { - worker = this.getWorker(applicant); - } else { - return true; + if (this.localWorker) { + return this.localWorker.execute(applicant, command); } - this.applicantWorkerMap.delete(applicant); - } else { - worker = this.getWorker(applicant); + if (this.applicantWorkerMap.has(applicant)) { + const worker = this.getWorker(applicant); + this.applicantWorkerMap.delete(applicant); + return worker.execute(applicant, command); + } + return true; } + const worker = this.getWorker(applicant); return worker.execute(applicant, command); } - private reset() { - this.workersPool.clear(); - - this.applicantWorkerMap.clear(); - - this.externalPlugin = this.defaultExternalPlugin; - - this.lastWorkerIndex = -1; - - this.workerLimit = 1; - } - public async kill(): Promise { + if (this.workerLimit === 'local' && this.localWorker) { + await this.localWorker.kill(); + this.localWorker = null; + return; + } + const workersToKill = [...this.workersPool.values()].map((worker) => worker.kill(), ); @@ -145,6 +142,10 @@ export class BrowserProxyController logger.error('Exit failed ', err); } - this.reset(); + this.workersPool.clear(); + this.applicantWorkerMap.clear(); + this.externalPlugin = this.defaultExternalPlugin; + this.lastWorkerIndex = -1; + this.workerLimit = 1; } } diff --git a/packages/browser-proxy/src/browser-proxy-local-worker.ts b/packages/browser-proxy/src/browser-proxy-local-worker.ts new file mode 100644 index 00000000..a31b68ee --- /dev/null +++ b/packages/browser-proxy/src/browser-proxy-local-worker.ts @@ -0,0 +1,120 @@ +import { + BrowserProxyActions, + BrowserProxyMessageTypes, + IBrowserProxyCommand, + IBrowserProxyCommandResponse, + IBrowserProxyPendingCommand, + IBrowserProxyWorker, + IBrowserProxyWorkerConfig, + ITransport, +} from '@testring/types'; +import {generateUniqId} from '@testring/utils'; +import {loggerClient} from '@testring/logger'; +import {BrowserProxy} from './browser-proxy/browser-proxy'; + +export class BrowserProxyLocalWorker implements IBrowserProxyWorker { + private pendingCommandsPool: Map = + new Map(); + protected browserProxy: BrowserProxy; + private logger = loggerClient.withPrefix('[browser-proxy-local-worker]'); + private workerID = `proxy-local-${generateUniqId()}`; + private removeHandlers: Array<() => void> = []; + + constructor( + private transport: ITransport, + private spawnConfig: IBrowserProxyWorkerConfig, + ) { + this.browserProxy = new BrowserProxy( + this.transport, + this.spawnConfig.plugin, + this.spawnConfig.config, + ); + this.registerTransportHandlers(); + } + + private registerTransportHandlers(): void { + this.removeHandlers.push( + this.transport.on( + BrowserProxyMessageTypes.response, + (response, source) => { + this.onCommandResponse(response); + }, + ), + ); + + this.removeHandlers.push( + this.transport.on( + BrowserProxyMessageTypes.exception, + (error, source) => { + this.kill().catch((err) => this.logger.error(err)); + throw error; + }, + ), + ); + } + + private onCommandResponse( + commandResponse: IBrowserProxyCommandResponse, + ): void { + const {uid, response, error} = commandResponse; + const item = this.pendingCommandsPool.get(uid); + + if (item) { + const {resolve, reject} = item; + this.pendingCommandsPool.delete(uid); + error ? reject(error) : resolve(response); + } else { + this.logger.error(`Cannot find command with uid ${uid}`); + throw new ReferenceError(`Cannot find command with uid ${uid}`); + } + } + + private async send(item: IBrowserProxyPendingCommand) { + const {command, applicant, uid} = item; + this.pendingCommandsPool.set(uid, item); + this.transport.broadcastUniversally(BrowserProxyMessageTypes.execute, { + uid, + command, + applicant, + }); + } + + public async spawn(): Promise { + this.logger.debug( + `Local browser proxy worker spawned with ID: ${this.workerID}`, + ); + } + + public async execute( + applicant: string, + command: IBrowserProxyCommand, + ): Promise { + return new Promise((resolve, reject) => { + const uid = generateUniqId(); + const item: IBrowserProxyPendingCommand = { + uid, + resolve, + reject, + command, + applicant, + }; + this.send(item); + }); + } + + public async kill(): Promise { + await this.execute('root', { + action: BrowserProxyActions.kill, + args: [], + }); + this.removeHandlers.forEach((removeHandler) => removeHandler()); + this.removeHandlers = []; + this.browserProxy.removeHandlers.forEach((removeHandler) => + removeHandler(), + ); + this.browserProxy.removeHandlers = []; + this.logger.debug( + `BrowserProxyLocalWorker with ID ${this.workerID} terminated.`, + ); + } +} diff --git a/packages/browser-proxy/src/browser-proxy-worker.ts b/packages/browser-proxy/src/browser-proxy-worker.ts index 9773fcdc..e6ff4e21 100644 --- a/packages/browser-proxy/src/browser-proxy-worker.ts +++ b/packages/browser-proxy/src/browser-proxy-worker.ts @@ -16,10 +16,8 @@ import {loggerClient} from '@testring/logger'; export class BrowserProxyWorker implements IBrowserProxyWorker { private pendingCommandsQueue: Set = new Set(); - private pendingCommandsPool: Map< - string, - IBrowserProxyPendingCommand - > = new Map(); + private pendingCommandsPool: Map = + new Map(); private spawnPromise: Promise | null = null; diff --git a/packages/browser-proxy/src/browser-proxy/browser-proxy.ts b/packages/browser-proxy/src/browser-proxy/browser-proxy.ts index 763f898c..2c806939 100644 --- a/packages/browser-proxy/src/browser-proxy/browser-proxy.ts +++ b/packages/browser-proxy/src/browser-proxy/browser-proxy.ts @@ -26,6 +26,8 @@ export class BrowserProxy { private logger = loggerClient.withPrefix('[browser-proxy]'); + public removeHandlers: Array<() => void> = []; + constructor( private transportInstance: ITransport, pluginPath: string, @@ -48,7 +50,7 @@ export class BrowserProxy { try { this.plugin = pluginFactory(pluginConfig); } catch (error) { - this.transportInstance.broadcast( + this.transportInstance.broadcastUniversally( BrowserProxyMessageTypes.exception, error, ); @@ -57,15 +59,21 @@ export class BrowserProxy { } private registerCommandListener() { - this.transportInstance.on(BrowserProxyMessageTypes.execute, (message) => - this.onMessage(message), + this.removeHandlers.push( + this.transportInstance.on( + BrowserProxyMessageTypes.execute, + (message) => this.onMessage(message), + ), ); } private sendEmptyResponse(uid: string) { - this.transportInstance.broadcast(BrowserProxyMessageTypes.response, { - uid, - }); + this.transportInstance.broadcastUniversally( + BrowserProxyMessageTypes.response, + { + uid, + }, + ); } private async onMessage(message: IBrowserProxyMessage) { @@ -97,7 +105,7 @@ export class BrowserProxy { ...command.args, ); - this.transportInstance.broadcast( + this.transportInstance.broadcastUniversally( BrowserProxyMessageTypes.response, { uid, @@ -106,7 +114,7 @@ export class BrowserProxy { }, ); } catch (error) { - this.transportInstance.broadcast( + this.transportInstance.broadcastUniversally( BrowserProxyMessageTypes.response, { uid, diff --git a/packages/browser-proxy/test/browser-proxy-local-controller.spec.ts b/packages/browser-proxy/test/browser-proxy-local-controller.spec.ts new file mode 100644 index 00000000..e7c08cda --- /dev/null +++ b/packages/browser-proxy/test/browser-proxy-local-controller.spec.ts @@ -0,0 +1,177 @@ +import {Transport} from '@testring/transport'; +import {BrowserProxyController} from '../src/browser-proxy-controller'; +import {fork} from '@testring/child-process'; +import * as path from 'path'; +import {BrowserProxyActions, BrowserProxyPlugins} from '@testring/types'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; + +const workerPath = path.resolve(__dirname, './fixtures/worker.ts'); +const asyncPlugin = path.resolve(__dirname, './fixtures/async-plugin.ts'); +const syncPlugin = path.resolve(__dirname, './fixtures/sync-plugin.ts'); + +describe('Browser Proxy Controller functional test for local worker', () => { + it('should not call workerCreator if workerLimit is set to local', async () => { + const workerCreatorMock = sinon.spy(); + const transport = new Transport(); + const controller = new BrowserProxyController( + transport, + workerCreatorMock, + ); + const hooks = controller.getHook(BrowserProxyPlugins.getPlugin); + + if (hooks) { + hooks.writeHook('test', () => { + return { + plugin: syncPlugin, + config: { + workerLimit: 'local', + }, + }; + }); + } + + await controller.init(); + + await controller.execute('test', { + action: BrowserProxyActions.click, + args: [], + }); + + chai.expect(workerCreatorMock.notCalled).to.be.equal(true); + + await controller.kill(); + }); + + it('should throw exception if proxy response contains non-empty "exception" field', async () => { + const workerCreatorMock = sinon.spy(); + const transport = new Transport(); + const controller = new BrowserProxyController( + transport, + workerCreatorMock, + ); + const hooks = controller.getHook(BrowserProxyPlugins.getPlugin); + + if (hooks) { + hooks.writeHook('test', () => { + return { + plugin: syncPlugin, + config: { + workerLimit: 'local', + }, + }; + }); + } + + await controller.init(); + + try { + await controller.execute('test', { + action: 'barrelRoll' as BrowserProxyActions, + args: [], + }); + } catch (e) { + chai.expect(e).to.be.instanceOf(Error); + } + + await controller.kill(); + }); + + it('should be able to run multiple workers in parallel', async () => { + const workerCreatorMock = sinon.spy(); + const transport = new Transport(); + const controller = new BrowserProxyController( + transport, + workerCreatorMock, + ); + + const hooks = controller.getHook(BrowserProxyPlugins.getPlugin); + + if (hooks) { + hooks.writeHook('test', () => { + return { + plugin: syncPlugin, + config: { + workerLimit: 'local', + }, + }; + }); + } + + await controller.init(); + + const testResult1 = await controller.execute('test1', { + action: BrowserProxyActions.click, + args: ['testResult'], + }); + + chai.expect(testResult1).to.be.equal('testResult'); + + const testResult2 = await controller.execute('test2', { + action: BrowserProxyActions.click, + args: [ + { + key: 'value', + }, + ], + }); + + chai.expect(testResult2).to.be.deep.equal({ + key: 'value', + }); + + const testResult3 = await controller.execute('test3', { + action: BrowserProxyActions.click, + args: [[1, 2, 3]], + }); + chai.expect(testResult3).to.be.deep.equal([1, 2, 3]); + + await controller.kill(); + }); + + it('should be able to run multiple workers in parallel', async () => { + const transport = new Transport(); + const controller = new BrowserProxyController( + transport, + (onActionPluginPath, config) => { + return fork(workerPath, [ + '--name', + onActionPluginPath, + '--config', + JSON.stringify(config), + ]); + }, + ); + const hooks = controller.getHook(BrowserProxyPlugins.getPlugin); + + if (hooks) { + hooks.writeHook('test', () => { + return { + plugin: asyncPlugin, + config: { + workerLimit: 'local', + }, + }; + }); + } + + await controller.init(); + + const executePromises = Array.from({length: 100}, (_, i) => { + return controller.execute(`test${i}`, { + action: BrowserProxyActions.click, + args: [Array.from({length: i * 10}, (__, j) => j)], + }); + }); + + const results = await Promise.all(executePromises); + + for (let i = 0; i < 100; i++) { + chai.expect(results[i]).to.be.deep.equal( + Array.from({length: i * 10}, (__, j) => j), + ); + } + + await controller.kill(); + }); +}); diff --git a/packages/browser-proxy/tsconfig.json b/packages/browser-proxy/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/browser-proxy/tsconfig.json +++ b/packages/browser-proxy/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/client-ws-transport/src/ws-transport.ts b/packages/client-ws-transport/src/ws-transport.ts index 9f672360..014a740d 100644 --- a/packages/client-ws-transport/src/ws-transport.ts +++ b/packages/client-ws-transport/src/ws-transport.ts @@ -18,7 +18,8 @@ interface IQueuedMessage { export class ClientWsTransport extends EventEmitter - implements IClientWsTransport { + implements IClientWsTransport +{ private url: string; constructor( diff --git a/packages/client-ws-transport/tsconfig.json b/packages/client-ws-transport/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/client-ws-transport/tsconfig.json +++ b/packages/client-ws-transport/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/devtool-backend/src/devtool-server-controller.ts b/packages/devtool-backend/src/devtool-server-controller.ts index aea2d9ef..b7bdcbda 100644 --- a/packages/devtool-backend/src/devtool-server-controller.ts +++ b/packages/devtool-backend/src/devtool-server-controller.ts @@ -24,7 +24,8 @@ import {extensionId} from '@testring/devtool-extension'; export class DevtoolServerController extends PluggableModule - implements IDevtoolServerController { + implements IDevtoolServerController +{ private workerID: string; private worker: IChildProcessFork; diff --git a/packages/devtool-backend/tsconfig.json b/packages/devtool-backend/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/devtool-backend/tsconfig.json +++ b/packages/devtool-backend/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/devtool-extension/package.json b/packages/devtool-extension/package.json index 7de03917..6b5acd0c 100644 --- a/packages/devtool-extension/package.json +++ b/packages/devtool-extension/package.json @@ -21,6 +21,7 @@ "@types/webpack": "5.28.5", "chrome-launcher": "1.1.2", "copy-webpack-plugin": "12.0.2", + "ignore-loader": "0.1.2", "os-browserify": "0.3.0", "path-browserify": "1.0.1", "ts-loader": "9.5.1", diff --git a/packages/devtool-extension/src/extension/element-highlight-controller.ts b/packages/devtool-extension/src/extension/element-highlight-controller.ts index 465ebc0b..065c83f9 100644 --- a/packages/devtool-extension/src/extension/element-highlight-controller.ts +++ b/packages/devtool-extension/src/extension/element-highlight-controller.ts @@ -194,7 +194,8 @@ export class ElementHighlightController { for (const htmlElement of addedElements) { if (unlinkedHighlights.length > 0) { - const usedHighlight = unlinkedHighlights.shift() as HighlightElement; + const usedHighlight = + unlinkedHighlights.shift() as HighlightElement; this.highlightElements.set(htmlElement, usedHighlight); diff --git a/packages/devtool-extension/tsconfig.json b/packages/devtool-extension/tsconfig.json index 42ef70ca..71b92855 100644 --- a/packages/devtool-extension/tsconfig.json +++ b/packages/devtool-extension/tsconfig.json @@ -10,7 +10,7 @@ "preserveConstEnums": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/devtool-extension/webpack.config.ts b/packages/devtool-extension/webpack.config.ts index 0c755d3b..742d381b 100644 --- a/packages/devtool-extension/webpack.config.ts +++ b/packages/devtool-extension/webpack.config.ts @@ -32,17 +32,21 @@ const config: webpack.Configuration = { resolve: { extensions: ['.tsx', '.ts', '.js'], - fallback:{ - net:false, + fallback: { + net: false, fs: false, - "path": require.resolve("path-browserify"), - "events": require.resolve("events/"), - "os": require.resolve("os-browserify/browser"), + path: require.resolve('path-browserify'), + events: require.resolve('events/'), + os: require.resolve('os-browserify/browser'), }, }, module: { rules: [ + { + test: /\.js\.map$/, + use: 'ignore-loader', + }, { test: /\.tsx?$/, use: { @@ -66,8 +70,11 @@ const config: webpack.Configuration = { { from: staticRelativeDir, to: outputDir, - transform(content, absolutePath){ - const relativePath = path.relative(__dirname, absolutePath); + transform(content, absolutePath) { + const relativePath = path.relative( + __dirname, + absolutePath, + ); if (relativePath === manifestRelativePath) { const data = JSON.parse(content.toString()); diff --git a/packages/devtool-frontend/package.json b/packages/devtool-frontend/package.json index e52cdad1..c2ca027d 100644 --- a/packages/devtool-frontend/package.json +++ b/packages/devtool-frontend/package.json @@ -19,6 +19,7 @@ "@types/react-dom": "18.3.1", "@types/webpack": "5.28.5", "css-loader": "7.1.2", + "ignore-loader": "0.1.2", "monaco-editor-webpack-plugin": "7.1.0", "os-browserify": "0.3.0", "path-browserify": "1.0.1", diff --git a/packages/devtool-frontend/tsconfig.json b/packages/devtool-frontend/tsconfig.json index 8435bd04..7d90a180 100644 --- a/packages/devtool-frontend/tsconfig.json +++ b/packages/devtool-frontend/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/devtool-frontend/webpack.config.ts b/packages/devtool-frontend/webpack.config.ts index 69441888..3219fbb1 100644 --- a/packages/devtool-frontend/webpack.config.ts +++ b/packages/devtool-frontend/webpack.config.ts @@ -12,12 +12,12 @@ const config: webpack.Configuration = { }, resolve: { extensions: ['.tsx', '.ts', '.js', '.css'], - fallback:{ - net:false, + fallback: { + net: false, fs: false, - "path": require.resolve("path-browserify"), - "events": require.resolve("events/"), - "os": require.resolve("os-browserify/browser"), + path: require.resolve('path-browserify'), + events: require.resolve('events/'), + os: require.resolve('os-browserify/browser'), }, }, plugins: [ @@ -27,6 +27,10 @@ const config: webpack.Configuration = { ], module: { rules: [ + { + test: /\.js\.map$/, + use: 'ignore-loader', + }, { test: /\.tsx?$/, use: 'ts-loader', diff --git a/packages/download-collector-crx/tsconfig.json b/packages/download-collector-crx/tsconfig.json index e3badf08..d74e3d3a 100644 --- a/packages/download-collector-crx/tsconfig.json +++ b/packages/download-collector-crx/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": ["es5", "es2015", "es2016", "es2017", "dom"], "noUnusedLocals": true, "strictNullChecks": true, diff --git a/packages/e2e-test-app/package.json b/packages/e2e-test-app/package.json index e610291c..2ebe7e96 100644 --- a/packages/e2e-test-app/package.json +++ b/packages/e2e-test-app/package.json @@ -12,22 +12,29 @@ "test": "echo concurrently -n simple,selenium \"npm run test:simple\" \"npm run test:selenium \" \"npm run test:screenshots\"", "test:all": "npm run test:simple && npm run test:selenium && npm run test:screenshots", "test:watch": "echo \"test:watch skipped\"", - "test:selenium": "ts-node src/test-runner.ts --config ./test/selenium/config.js", + "test:selenium": "ts-node src/test-runner.ts --config ./test/selenium/config.js --env-config=./test/selenium/env.json", + "test:selenium:headless": "ts-node src/test-runner.ts --config ./test/selenium/config.js --env-config=./test/selenium/env.json --headless", "test:screenshots": "ts-node src/test-runner.ts --config ./test/selenium/config-screenshot.js", "test:simple": "testring run --config test/simple/.testringrc --env-parameters.test 10 --rc.tags-list=#P0,#P1", "build": "echo \"build skipped\"", - "build:watch": "echo \"build:watch skipped\"" + "build:watch": "echo \"build:watch skipped\"", + "install:chrome": "npx @puppeteer/browsers install chrome@stable --path ./chrome-cache", + "install:chromedriver": "npx @puppeteer/browsers install chromedriver@stable --path ./chrome-cache" }, "dependencies": { + "@puppeteer/browsers": "2.7.0", "@testring/cli": "0.7.2", "@testring/plugin-babel": "0.7.2", "@testring/plugin-fs-store": "0.7.2", "@testring/plugin-selenium-driver": "0.7.2", "@testring/web-application": "0.7.2", "@types/express": "5.0.0", + "@types/multer": "1.4.12", "babel-preset-es2015": "6.24.1", + "c8": "10.1.3", "concurrently": "9.0.1", "express": "4.21.1", + "multer": "1.4.5-lts.1", "testring": "0.7.2", "ts-node": "10.9.2" } diff --git a/packages/e2e-test-app/src/mock-web-server.ts b/packages/e2e-test-app/src/mock-web-server.ts index dada6313..60c9e93f 100644 --- a/packages/e2e-test-app/src/mock-web-server.ts +++ b/packages/e2e-test-app/src/mock-web-server.ts @@ -1,16 +1,21 @@ import * as express from 'express'; import * as Http from 'http'; +import * as path from 'node:path'; +import * as multer from 'multer'; const port = 8080; +const upload = multer({storage: multer.memoryStorage()}); + export class MockWebServer { private httpServerInstance: Http.Server; start(): Promise { return new Promise((resolve) => { - this.httpServerInstance = MockWebServer.createExpressWebApplication().listen( - port, - resolve, - ); + this.httpServerInstance = + MockWebServer.createExpressWebApplication().listen( + port, + resolve, + ); }); } @@ -20,7 +25,20 @@ export class MockWebServer { private static createExpressWebApplication(): express.Application { const app = express(); - app.use(express.static('static-fixtures')); + app.use(express.static(path.join(__dirname, '..', 'static-fixtures'))); + + // POST upload endpoint + app.post('/upload', upload.single('file'), (req, res) => { + if (!req.file) { + res.status(400).json({error: 'No file uploaded'}); + return; + } + res.status(200).json({ + message: 'File received successfully', + filename: req.file.originalname, + }); + }); + return app; } } diff --git a/packages/e2e-test-app/src/test-runner.ts b/packages/e2e-test-app/src/test-runner.ts index b098fc77..ff4a4726 100644 --- a/packages/e2e-test-app/src/test-runner.ts +++ b/packages/e2e-test-app/src/test-runner.ts @@ -1,16 +1,19 @@ import * as childProcess from 'child_process'; import {MockWebServer} from './mock-web-server'; +import * as path from 'node:path'; const mockWebServer = new MockWebServer(); const filenameArgIndex = process.argv.indexOf(__filename); const args = process.argv.slice(filenameArgIndex + 1); +const testringDir = path.resolve(require.resolve('testring'), '..', '..'); +const testringFile = path.resolve(testringDir, 'bin', 'testring.js'); async function runTests() { await mockWebServer.start(); const testringProcess = childProcess.exec( - `./node_modules/.bin/testring ${args.join(' ')}`, + `node ${testringFile} ${args.join(' ')}`, {}, (error, stdout, stderr) => { mockWebServer.stop(); @@ -28,6 +31,16 @@ async function runTests() { if (testringProcess.stderr) { testringProcess.stderr.pipe(process.stderr); } + + testringProcess.on('unhandledRejection', (reason, promise) => { + // eslint-disable-next-line no-console + console.error('Unhandled Rejection at:', promise, 'reason:', reason); + }); + + testringProcess.on('uncaughtException', (error) => { + // eslint-disable-next-line no-console + console.error('Uncaught Exception:', error); + }); } runTests().catch(() => process.exit(1)); diff --git a/packages/e2e-test-app/static-fixtures/click.html b/packages/e2e-test-app/static-fixtures/click.html index 8fe213d2..1274e283 100644 --- a/packages/e2e-test-app/static-fixtures/click.html +++ b/packages/e2e-test-app/static-fixtures/click.html @@ -5,35 +5,64 @@ Click test - -

- -
- -
-
-

- -
- -
-
-

- + +

+ +
+ +
+
+

+ +
+ +
+
+

+ + +

Click count: 0

+ + +
+ +
+
- + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/drag-and-drop.html b/packages/e2e-test-app/static-fixtures/drag-and-drop.html new file mode 100644 index 00000000..bfa9acd1 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/drag-and-drop.html @@ -0,0 +1,101 @@ + + + + + Drag and Drop Test + + + +
Drag 1
+
Drag 2
+
Drag 3
+ +
Drop Area 1
+
Drop Area 2
+
Drop Area 3
+ + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/elements.html b/packages/e2e-test-app/static-fixtures/elements.html index bfc31a91..c79dee20 100644 --- a/packages/e2e-test-app/static-fixtures/elements.html +++ b/packages/e2e-test-app/static-fixtures/elements.html @@ -5,12 +5,15 @@ Elements test -
    -
  • -
  • -
  • -
  • -
  • -
+
    +
  • +
  • +
  • +
  • +
  • +
+ + + - + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/focus-stable.html b/packages/e2e-test-app/static-fixtures/focus-stable.html new file mode 100644 index 00000000..68af02cc --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/focus-stable.html @@ -0,0 +1,40 @@ + + + + + WebdriverIO Test + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/frame.html b/packages/e2e-test-app/static-fixtures/frame.html new file mode 100644 index 00000000..933091de --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/frame.html @@ -0,0 +1,32 @@ + + + + + Iframe Test + + + +
Content of the main page
+ + + + + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/get-size.html b/packages/e2e-test-app/static-fixtures/get-size.html new file mode 100644 index 00000000..7f5d8997 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/get-size.html @@ -0,0 +1,12 @@ + + + + + Get Size + + + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/get-source.html b/packages/e2e-test-app/static-fixtures/get-source.html new file mode 100644 index 00000000..7906c6d0 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/get-source.html @@ -0,0 +1,11 @@ + + + + + Get Source + + + + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/iframe1.html b/packages/e2e-test-app/static-fixtures/iframe1.html new file mode 100644 index 00000000..65923430 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/iframe1.html @@ -0,0 +1,10 @@ + + + + + Iframe 1 + + +
Content of Iframe 1
+ + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/iframe2.html b/packages/e2e-test-app/static-fixtures/iframe2.html new file mode 100644 index 00000000..aaf8bbbb --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/iframe2.html @@ -0,0 +1,10 @@ + + + + + Iframe 2 + + +
Content of Iframe 2
+ + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/timezone.html b/packages/e2e-test-app/static-fixtures/timezone.html new file mode 100644 index 00000000..b05a9b7a --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/timezone.html @@ -0,0 +1,22 @@ + + + + + Browser Timezone + + +
+ Current Browser Timezone: +

+ Loading... +

+
+ + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/upload.html b/packages/e2e-test-app/static-fixtures/upload.html new file mode 100644 index 00000000..53149cc3 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/upload.html @@ -0,0 +1,47 @@ + + + + + File Upload Test + + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/wait-for-visible.html b/packages/e2e-test-app/static-fixtures/wait-for-visible.html new file mode 100644 index 00000000..57fc4dc8 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/wait-for-visible.html @@ -0,0 +1,85 @@ + + + + + Wait for visible + + + + +
+ +
+

This is the info section.

+ +
+
+ + \ No newline at end of file diff --git a/packages/e2e-test-app/static-fixtures/wait-until.html b/packages/e2e-test-app/static-fixtures/wait-until.html new file mode 100644 index 00000000..0e4e73c3 --- /dev/null +++ b/packages/e2e-test-app/static-fixtures/wait-until.html @@ -0,0 +1,69 @@ + + + + + Wait Until Page + + + + +
+ + +
+
+ + +
+ + \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/config.coverage.js b/packages/e2e-test-app/test/selenium/config.coverage.js new file mode 100644 index 00000000..362890ce --- /dev/null +++ b/packages/e2e-test-app/test/selenium/config.coverage.js @@ -0,0 +1,67 @@ +/* eslint-disable */ +const browsers = require('@puppeteer/browsers'); +const path = require('path'); + +module.exports = async (config) => { + const info = await browsers.getInstalledBrowsers({ + cacheDir: process.env['CHROME_CACHE_DIR'] || path.join(__dirname, '..', '..', 'chrome-cache'), + }); + const chrome = info.find((item) => item.browser === 'chrome'); + const chromedriver = info.find((item) => item.browser === 'chromedriver'); + console.log('Chrome:', chrome.executablePath); + console.log('Chromedriver:', chromedriver.executablePath); + const local = !config.headless; + + const babelConfig = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + }, + ], + ], + }; + + if (config.debug) { + babelConfig.presets[0][1].debug = true; + babelConfig.sourceMaps = 'inline'; + } + + return { + screenshotPath: './_tmp/', + workerLimit: 'local', + maxWriteThreadCount: 2, + screenshots: 'disable', + logLevel: 'verbose', + retryCount: 0, + testTimeout: local ? 0 : config.testTimeout, + tests: 'packages/e2e-test-app/test/selenium/test/**/*.spec.js', + plugins: [ + [ + 'selenium-driver', + { + clientTimeout: 60000, + path: '/wd/hub', + chromeDriverPath: process.env['CHROMEDRIVER_PATH'] || chromedriver.executablePath, + workerLimit: 'local', + capabilities: local + ? { + 'goog:chromeOptions': { + binary: process.env['CHROME_BIN'] || chrome.executablePath, + }, + } + : { + 'goog:chromeOptions': { + binary: process.env['CHROME_BIN'] || chrome.executablePath, + args: ['--headless=new', '--no-sandbox'], + }, + }, + }, + ], + ['babel', babelConfig], + ], + }; +}; diff --git a/packages/e2e-test-app/test/selenium/config.js b/packages/e2e-test-app/test/selenium/config.js index c87c6937..63ba861d 100644 --- a/packages/e2e-test-app/test/selenium/config.js +++ b/packages/e2e-test-app/test/selenium/config.js @@ -1,36 +1,68 @@ +const browsers = require('@puppeteer/browsers'); +const path = require('path'); + module.exports = async (config) => { - const devtool = config.debug || config.devtool; + const info = await browsers.getInstalledBrowsers({ + cacheDir: path.join(__dirname, '..', '..', 'chrome-cache'), + }); + const chrome = info.find((item) => item.browser === 'chrome'); + if (!chrome) { + throw new Error('Chrome is not found'); + } + const chromedriver = info.find((item) => item.browser === 'chromedriver'); + if (!chromedriver) { + throw new Error('Chromedriver is not found'); + } + const local = !config.headless; + + const babelConfig = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + }, + ], + ], + }; + + if (config.debug) { + babelConfig.presets[0][1].debug = true; + babelConfig.sourceMaps = 'inline'; + } return { - devtool, screenshotPath: './_tmp/', - workerLimit: devtool ? 'local' : 5, + workerLimit: local ? 'local' : 5, maxWriteThreadCount: 2, screenshots: 'disable', retryCount: 0, - testTimeout: devtool ? 0 : config.testTimeout, - tests: 'test/selenium/test/*.spec.js', + testTimeout: local ? 0 : config.testTimeout, + tests: 'test/selenium/test/**/*.spec.js', plugins: [ [ 'selenium-driver', { - clientTimeout: devtool ? 0 : config.testTimeout, - recorderExtension: devtool, + clientTimeout: local ? 0 : config.testTimeout, path: '/wd/hub', - capabilities: devtool - ? {} + chromeDriverPath: chromedriver.executablePath, + capabilities: local + ? { + 'goog:chromeOptions': { + binary: chrome.executablePath, + }, + } : { 'goog:chromeOptions': { - args: [ - '--headless', - '--disable-gpu', - '--no-sandbox', - ], + binary: chrome.executablePath, + args: ['--headless=new', '--no-sandbox'], }, }, }, ], - ['babel'], + ['babel', babelConfig], ], }; }; diff --git a/packages/e2e-test-app/test/selenium/env.json b/packages/e2e-test-app/test/selenium/env.json new file mode 100644 index 00000000..c2b3bff4 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/env.json @@ -0,0 +1,5 @@ +{ + "envParameters" : { + "baseUrl" : "http://localhost:8080/" + } +} \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/alert.spec.js b/packages/e2e-test-app/test/selenium/test/alert.spec.js index b9ec231f..e8cbcfa8 100644 --- a/packages/e2e-test-app/test/selenium/test/alert.spec.js +++ b/packages/e2e-test-app/test/selenium/test/alert.spec.js @@ -1,28 +1,30 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/alert.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'alert.html')); - if (await api.application.isAlertOpen()) { - await api.application.alertAccept(); + if (await app.isAlertOpen()) { + await app.alertAccept(); } else { throw Error('Alert is not opened'); } - if (await api.application.isAlertOpen()) { - await api.application.alertDismiss(); + if (await app.isAlertOpen()) { + await app.alertDismiss(); } else { throw Error('Alert is not opened'); } - const text = await api.application.alertText(); + const text = await app.alertText(); - const firstAlertState = await api.application.getText(api.application.root.alerts.first); - const secondAlertState = await api.application.getText(api.application.root.alerts.second); - const thirdAlertState = await api.application.getText(api.application.root.alerts.third); + const firstAlertState = await app.getText(app.root.alerts.first); + const secondAlertState = await app.getText(app.root.alerts.second); + const thirdAlertState = await app.getText(app.root.alerts.third); - api.application.assert.equal(firstAlertState, 'true'); - api.application.assert.equal(secondAlertState, 'false'); - api.application.assert.equal(thirdAlertState, 'false'); - api.application.assert.equal(text, 'test'); + await app.assert.equal(firstAlertState, 'true'); + await app.assert.equal(secondAlertState, 'false'); + await app.assert.equal(thirdAlertState, 'false'); + await app.assert.equal(text, 'test'); }); diff --git a/packages/e2e-test-app/test/selenium/test/click.spec.js b/packages/e2e-test-app/test/selenium/test/click.spec.js index 99d44786..0fb3c91d 100644 --- a/packages/e2e-test-app/test/selenium/test/click.spec.js +++ b/packages/e2e-test-app/test/selenium/test/click.spec.js @@ -1,33 +1,61 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/click.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'click.html')); - await api.application.click(api.application.root.button); + await app.click(app.root.button); - const outputText = await api.application.getText(api.application.root.output); - api.application.assert.equal(outputText, 'success'); + const outputText = await app.getText(app.root.output); + await app.assert.equal(outputText, 'success'); try { - await api.application.clickCoordinates(api.application.root.halfHoveredButton, { x: 0, y: 0 }); + await app.clickCoordinates(app.root.halfHoveredButton, {x: 0, y: 0}); throw Error('Test failed'); - } catch (e) { /* ignore */ } + } catch (e) { + /* ignore */ + } - await api.application.click(api.application.root.halfHoveredOverlay); - await api.application.clickCoordinates(api.application.root.halfHoveredButton, { x: 'right', y: 'center' }); - - const halfHoveredOutputText = await api.application.getText(api.application.root.halfHoveredOutput); - api.application.assert.equal(halfHoveredOutputText, 'success'); + await app.click(app.root.halfHoveredOverlay); + await app.clickCoordinates(app.root.halfHoveredButton, { + x: 'right', + y: 'center', + }); + const halfHoveredOutputText = await app.getText(app.root.halfHoveredOutput); + await app.assert.equal(halfHoveredOutputText, 'success'); try { - await api.application.clickCoordinates(api.application.root.partiallyHoveredButton, { x: 0, y: 0 }); + await app.clickCoordinates(app.root.partiallyHoveredButton, { + x: 0, + y: 0, + }); throw Error('Test failed'); - } catch (e) { /* ignore */ } - - await api.application.click(api.application.root.partiallyHoveredOverlay); - await api.application.clickButton(api.application.root.partiallyHoveredButton); - - const partiallyHoveredOutputText = await api.application.getText(api.application.root.partiallyHoveredOutput); - api.application.assert.equal(partiallyHoveredOutputText, 'success'); + } catch (e) { + /* ignore */ + } + + await app.click(app.root.partiallyHoveredOverlay); + await app.clickButton(app.root.partiallyHoveredButton); + + const partiallyHoveredOutputText = await app.getText( + app.root.partiallyHoveredOutput, + ); + await app.assert.equal(partiallyHoveredOutputText, 'success'); + + // doubleClick + await app.click(app.root.clickCounterButton); + await app.doubleClick(app.root.clickCounterButton); + await app.doubleClick(app.root.clickCounterButton); + const clicksCount = await app.getText(app.root.clickCountOutput); + await app.assert.equal(clicksCount, 'Click count: 5'); + + // isClickable, waitForClickable + let isClickable = await app.isClickable(app.root.clickableButton); + await app.assert.equal(isClickable, false); + await app.click(app.root.manageClickableStateButton); + await app.waitForClickable(app.root.clickableButton); + isClickable = await app.isClickable(app.root.clickableButton); + await app.assert.equal(isClickable, true); }); diff --git a/packages/e2e-test-app/test/selenium/test/cookie.spec.js b/packages/e2e-test-app/test/selenium/test/cookie.spec.js index 8a3bdf62..e13d1201 100644 --- a/packages/e2e-test-app/test/selenium/test/cookie.spec.js +++ b/packages/e2e-test-app/test/selenium/test/cookie.spec.js @@ -1,34 +1,39 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/cookie.html'); - const cookieValue = await api.application.getCookie('test'); - await api.application.assert.equal(cookieValue, 'TestData'); + let app = api.application; + await app.url(getTargetUrl(api, 'cookie.html')); + const cookieValue = await app.getCookie('test'); + await app.assert.equal(cookieValue, 'TestData'); - await api.application.deleteCookie('test'); - await api.application.click(api.application.root.cookie_clear_button); + await app.deleteCookie('test'); + await app.click(app.root.cookie_clear_button); - const cookieTextAfterDelete = await api.application.getText(api.application.root.cookie_found_text); - await api.application.assert.equal(cookieTextAfterDelete, ''); + const cookieTextAfterDelete = await app.getText(app.root.cookie_found_text); + await app.assert.equal(cookieTextAfterDelete, ''); - const cookieTextGetter = await api.application.getCookie('test'); - await api.application.assert.equal(cookieTextGetter, undefined); + const cookieTextGetter = await app.getCookie('test'); + await app.assert.equal(cookieTextGetter, undefined); - await api.application.setCookie({ 'name': 'foo', 'value': '1111' }); - const cookieValueAfterAdd = await api.application.getCookie('foo'); - await api.application.assert.equal(cookieValueAfterAdd, '1111'); + await app.setCookie({name: 'foo', value: '1111'}); + const cookieValueAfterAdd = await app.getCookie('foo'); + await app.assert.equal(cookieValueAfterAdd, '1111'); - const allCookies = await api.application.getCookie(); - const expected = [{ - 'domain':'localhost', - 'httpOnly':false, - 'name':'foo', - 'path':'/', - 'secure':false, - 'value':'1111', - }]; - await api.application.assert.deepEqual(allCookies, expected); - await api.application.deleteCookie(); - const allCookiesAfterDelete = await api.application.getCookie(); - await api.application.assert.deepEqual(allCookiesAfterDelete, []); + const allCookies = await app.getCookie(); + const expected = [ + { + domain: 'localhost', + httpOnly: false, + name: 'foo', + path: '/', + secure: false, + value: '1111', + sameSite: 'Lax', + }, + ]; + await app.assert.deepEqual(allCookies, expected); + await app.deleteCookie(); + const allCookiesAfterDelete = await app.getCookie(); + await app.assert.deepEqual(allCookiesAfterDelete, []); }); diff --git a/packages/e2e-test-app/test/selenium/test/css.spec.js b/packages/e2e-test-app/test/selenium/test/css.spec.js index 3f6bb3a9..c081948b 100644 --- a/packages/e2e-test-app/test/selenium/test/css.spec.js +++ b/packages/e2e-test-app/test/selenium/test/css.spec.js @@ -1,37 +1,59 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/css.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'css.html')); - const bgColorFromClass = await api.application.getCssProperty(api.application.root.withClass, 'background-color'); - const bgColorFromStyle = await api.application.getCssProperty(api.application.root.withStyle, 'background-color'); + const bgColorFromClass = await app.getCssProperty( + app.root.withClass, + 'background-color', + ); + const bgColorFromStyle = await app.getCssProperty( + app.root.withStyle, + 'background-color', + ); - await api.application.assert.equal(bgColorFromClass, 'rgba(139,0,0,1)'); // necessary what css-property have 'darkred' value in html - await api.application.assert.equal(bgColorFromStyle, 'rgba(255,0,0,1)'); // necessary what css-property have 'red' value in html + await app.assert.equal(bgColorFromClass, 'rgba(139,0,0,1)'); // necessary what css-property have 'darkred' value in html + await app.assert.equal(bgColorFromStyle, 'rgba(255,0,0,1)'); // necessary what css-property have 'red' value in html - const isCSSClassExistsFalse = await api.application.isCSSClassExists( - api.application.root.withStyle, 'customDivClass', + const isCSSClassExistsFalse = await app.isCSSClassExists( + app.root.withStyle, + 'customDivClass', ); - const isCSSClassExistsTrue = await api.application.isCSSClassExists( - api.application.root.withClass, 'customDivClass', + const isCSSClassExistsTrue = await app.isCSSClassExists( + app.root.withClass, + 'customDivClass', ); - await api.application.assert.equal(isCSSClassExistsFalse, false); - await api.application.assert.equal(isCSSClassExistsTrue, true); + await app.assert.equal(isCSSClassExistsFalse, false); + await app.assert.equal(isCSSClassExistsTrue, true); // ------------------------------------------------------------------------------ - const isBecomeVisibleElementVisibleNow = await api.application.isVisible(api.application.root.becomeVisible); - const isBecomeHiddenElementVisibleNow = await api.application.isVisible(api.application.root.becomeHidden); + const isBecomeVisibleElementVisibleNow = await app.isVisible( + app.root.becomeVisible, + ); + const isBecomeHiddenElementVisibleNow = await app.isVisible( + app.root.becomeHidden, + ); - await api.application.assert.equal(isBecomeVisibleElementVisibleNow, false); - await api.application.assert.equal(isBecomeHiddenElementVisibleNow, true); + await app.assert.equal(isBecomeVisibleElementVisibleNow, false); + await app.assert.equal(isBecomeHiddenElementVisibleNow, true); - await api.application.click(api.application.root.hideVisibleButton); - const becomeHidden = await api.application.isBecomeHidden(api.application.root.becomeHidden); - await api.application.assert.equal(becomeHidden, true, 'becomeHidden item should become hidden'); + await app.click(app.root.hideVisibleButton); + const becomeHidden = await app.isBecomeHidden(app.root.becomeHidden); + await app.assert.equal( + becomeHidden, + true, + 'becomeHidden item should become hidden', + ); - await api.application.click(api.application.root.showHiddenButton); - const becomeVisible = await api.application.isBecomeVisible(api.application.root.becomeVisible); - await api.application.assert.equal(becomeVisible, true, 'becomeVisible item should become visible'); + await app.click(app.root.showHiddenButton); + const becomeVisible = await app.isBecomeVisible(app.root.becomeVisible); + await app.assert.equal( + becomeVisible, + true, + 'becomeVisible item should become visible', + ); }); diff --git a/packages/e2e-test-app/test/selenium/test/drag-and-drop.spec.js b/packages/e2e-test-app/test/selenium/test/drag-and-drop.spec.js new file mode 100644 index 00000000..ace079f9 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/drag-and-drop.spec.js @@ -0,0 +1,19 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'drag-and-drop.html')); + + let isDraggable1Visible = await app.isVisible(app.root.draggable1); + let isDropZone2Visible = await app.isVisible(app.root.dropzone2); + + await app.assert.equal(isDraggable1Visible && isDropZone2Visible, true); + + await app.dragAndDrop(app.root.draggable1, app.root.dropzone2); + + let isDroppedElementVisible = await app.isVisible(app.root.dropzone2.draggable1); + + await app.assert.equal(isDroppedElementVisible, true); + +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/elements.spec.js b/packages/e2e-test-app/test/selenium/test/elements.spec.js index 51e78290..205aebdd 100644 --- a/packages/e2e-test-app/test/selenium/test/elements.spec.js +++ b/packages/e2e-test-app/test/selenium/test/elements.spec.js @@ -1,41 +1,75 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/elements.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'elements.html')); - const shouldBeFalse = await api.application.isElementsExist(api.application.root.unknownElement); - await api.application.assert.equal(shouldBeFalse, false); + const shouldBeFalse = await app.isElementsExist(app.root.unknownElement); + await app.assert.equal(shouldBeFalse, false); - const shouldBeTrue = await api.application.isElementsExist(api.application.root.existingElement); - await api.application.assert.equal(shouldBeTrue, true); + const shouldBeTrue = await app.isElementsExist(app.root.existingElement); + await app.assert.equal(shouldBeTrue, true); - const liExistingShouldBeTrue = await api.application.isElementsExist(api.application.root.existingElement.li); - await api.application.assert.equal(liExistingShouldBeTrue, true); + const liExistingShouldBeTrue = await app.isElementsExist( + app.root.existingElement.li, + ); + await app.assert.equal(liExistingShouldBeTrue, true); // ------------------------------------------------------------------------------------------------- - const notExistShouldBeTrue = await api.application.notExists(api.application.root.unknownElement); - await api.application.assert.equal(notExistShouldBeTrue, true); + const notExistShouldBeTrue = await app.notExists(app.root.unknownElement); + await app.assert.equal(notExistShouldBeTrue, true); - const notExistShouldBeFalse = await api.application.notExists(api.application.root.existingElement); - await api.application.assert.equal(notExistShouldBeFalse, false); + const notExistShouldBeFalse = await app.notExists(app.root.existingElement); + await app.assert.equal(notExistShouldBeFalse, false); // ------------------------------------------------------------------------------------------------- - const isExistingShouldBeTrue = await api.application.isExisting(api.application.root.existingElement); - await api.application.assert.equal(isExistingShouldBeTrue, true); + const isExistingShouldBeTrue = await app.isExisting( + app.root.existingElement, + ); + await app.assert.equal(isExistingShouldBeTrue, true); - const isExistingShouldBeFalse = await api.application.isExisting(api.application.root.unknownElement); - await api.application.assert.equal(isExistingShouldBeFalse, false); + const isExistingShouldBeFalse = await app.isExisting( + app.root.unknownElement, + ); + await app.assert.equal(isExistingShouldBeFalse, false); // ------------------------------------------------------------------------------------------------- - const unknownElementCount = await api.application.getElementsCount(await api.application.root.unknownElement); - await api.application.assert.equal(unknownElementCount, 0); + const unknownElementCount = await app.getElementsCount( + await app.root.unknownElement, + ); + await app.assert.equal(unknownElementCount, 0); - const elementCount = await api.application.getElementsCount(await api.application.root.existingElement); - await api.application.assert.equal(elementCount, 1); + const elementCount = await app.getElementsCount( + await app.root.existingElement, + ); + await app.assert.equal(elementCount, 1); + + const liElementsCount = await app.getElementsCount( + await app.root.existingElement.li, + ); + await app.assert.equal(liElementsCount, 5); + + // ------------------------------------------------------------------------------------------------- + + // getElementsIds + const elementsIds = [ + await app.getElementsIds(app.root.checkbox1), + await app.getElementsIds(app.root.checkbox2), + ]; + + await app.assert.ok(elementsIds.every((id) => typeof id === 'string')); + + // ------------------------------------------------------------------------------------------------- + + let checkedStates = []; + for (const id of elementsIds) { + checkedStates.push(await app.isElementSelected(id)); + } + + await app.assert.deepEqual(checkedStates, [false, true]); - const liElementsCount = await api.application.getElementsCount(await api.application.root.existingElement.li); - await api.application.assert.equal(liElementsCount, 5); }); diff --git a/packages/e2e-test-app/test/selenium/test/fixtures/LoremIpsum.pdf b/packages/e2e-test-app/test/selenium/test/fixtures/LoremIpsum.pdf new file mode 100644 index 00000000..0ba0b83b Binary files /dev/null and b/packages/e2e-test-app/test/selenium/test/fixtures/LoremIpsum.pdf differ diff --git a/packages/e2e-test-app/test/selenium/test/focus-stable.spec.js b/packages/e2e-test-app/test/selenium/test/focus-stable.spec.js new file mode 100644 index 00000000..314992ad --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/focus-stable.spec.js @@ -0,0 +1,29 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'focus-stable.html')); + + let isFocused = await app.isFocused(app.root.testInput); + await app.assert.notOk(isFocused); + await app.click(app.root.focusInputButton); + isFocused = await app.isFocused(app.root.testInput); + await app.assert.ok(isFocused); + + let isEnabled = await app.isEnabled(app.root.testButton); + await app.assert.notOk(isEnabled); + await app.click(app.root.enableButton); + await app.waitForEnabled(app.root.testButton); + isEnabled = await app.isEnabled(app.root.testButton); + await app.assert.ok(isEnabled); + + let isStable = await app.isStable(app.root.testDiv); + await app.assert.ok(isStable); + await app.click(app.root.stabilizeElementButton); + isStable = await app.isStable(app.root.testDiv); + await app.assert.notOk(isStable); + await app.waitForStable(app.root.testDiv); + isStable = await app.isStable(app.root.testDiv); + await app.assert.ok(isStable); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/form.spec.js b/packages/e2e-test-app/test/selenium/test/form.spec.js index e5f31623..43ddcbc0 100644 --- a/packages/e2e-test-app/test/selenium/test/form.spec.js +++ b/packages/e2e-test-app/test/selenium/test/form.spec.js @@ -1,59 +1,106 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/form.html'); - - const isEnabledForEnabled = await api.application.isEnabled(api.application.root.form.nameInput); - const isEnabledForDisabled = await api.application.isEnabled(api.application.root.form.disabledInput); - await api.application.assert.equal(isEnabledForEnabled, true); - await api.application.assert.equal(isEnabledForDisabled, false); - - const isDisabledForEnabled = await api.application.isDisabled(api.application.root.form.nameInput); - const isDisabledForDisabled = await api.application.isDisabled(api.application.root.form.disabledInput); - await api.application.assert.equal(isDisabledForEnabled, false); - await api.application.assert.equal(isDisabledForDisabled, true); - + let app = api.application; + await app.url(getTargetUrl(api, 'form.html')); + + const isEnabledForEnabled = await app.isEnabled(app.root.form.nameInput); + const isEnabledForDisabled = await app.isEnabled( + app.root.form.disabledInput, + ); + await app.assert.equal(isEnabledForEnabled, true); + await app.assert.equal(isEnabledForDisabled, false); + + const isDisabledForEnabled = await app.isDisabled(app.root.form.nameInput); + const isDisabledForDisabled = await app.isDisabled( + app.root.form.disabledInput, + ); + await app.assert.equal(isDisabledForEnabled, false); + await app.assert.equal(isDisabledForDisabled, true); + // readonly - const isReadonlyForWriteable = await api.application.isReadOnly(api.application.root.form.nameInput); - const isReadonlyForReadonly = await api.application.isReadOnly(api.application.root.form.readonlyInput); - await api.application.assert.equal(isReadonlyForReadonly, true); - await api.application.assert.equal(isReadonlyForWriteable, false); + const isReadonlyForWriteable = await app.isReadOnly( + app.root.form.nameInput, + ); + const isReadonlyForReadonly = await app.isReadOnly( + app.root.form.readonlyInput, + ); + await app.assert.equal(isReadonlyForReadonly, true); + await app.assert.equal(isReadonlyForWriteable, false); // checkbox - const isInitialCheckedShouldFalse = await api.application.isChecked(api.application.root.form.checkbox); - const isInitialCheckedShouldTrue = await api.application.isChecked(api.application.root.form.checkbox_2); + const isInitialCheckedShouldFalse = await app.isChecked( + app.root.form.checkbox, + ); + const isInitialCheckedShouldTrue = await app.isChecked( + app.root.form.checkbox_2, + ); - await api.application.assert.equal(isInitialCheckedShouldTrue, true); - await api.application.assert.equal(isInitialCheckedShouldFalse, false); + await app.assert.equal(isInitialCheckedShouldTrue, true); + await app.assert.equal(isInitialCheckedShouldFalse, false); - await api.application.setChecked(api.application.root.form.checkbox, true); - await api.application.setChecked(api.application.root.form.checkbox_2, false); + await app.setChecked(app.root.form.checkbox, true); + await app.setChecked(app.root.form.checkbox_2, false); - const afterSetCheckedIsCheckedShouldTrue = await api.application.isChecked(api.application.root.form.checkbox); - const afterSetCheckedIsCheckedShouldFalse = await api.application.isChecked(api.application.root.form.checkbox_2); + const afterSetCheckedIsCheckedShouldTrue = await app.isChecked( + app.root.form.checkbox, + ); + const afterSetCheckedIsCheckedShouldFalse = await app.isChecked( + app.root.form.checkbox_2, + ); - await api.application.assert.equal(afterSetCheckedIsCheckedShouldTrue, true); - await api.application.assert.equal(afterSetCheckedIsCheckedShouldFalse, false); + await app.assert.equal(afterSetCheckedIsCheckedShouldTrue, true); + await app.assert.equal(afterSetCheckedIsCheckedShouldFalse, false); // inputs - const initialValueOfEmptyInput = await api.application.getValue(api.application.root.form.nameInput); - const initialValueOfInputWithDefaultValue = await api.application.getValue(api.application.root.form.readonlyInput); + const initialValueOfEmptyInput = await app.getValue( + app.root.form.nameInput, + ); + const initialValueOfInputWithDefaultValue = await app.getValue( + app.root.form.readonlyInput, + ); + + await app.assert.equal(initialValueOfEmptyInput, ''); + await app.assert.equal(initialValueOfInputWithDefaultValue, 'readonly'); - await api.application.assert.equal(initialValueOfEmptyInput, ''); - await api.application.assert.equal(initialValueOfInputWithDefaultValue, 'readonly'); + await app.setValue(app.root.form.nameInput, 'testValue'); + let afterSetValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterSetValue, 'testValue'); - await api.application.setValue(api.application.root.form.nameInput, 'testValue'); - const afterSetValue = await api.application.getValue(api.application.root.form.nameInput); - await api.application.assert.equal(afterSetValue, 'testValue'); + await app.clearElement(app.root.form.nameInput); + let afterClearValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterClearValue, ''); - await api.application.clearElement(api.application.root.form.nameInput); - const afterClearValue = await api.application.getValue(api.application.root.form.nameInput); - await api.application.assert.equal(afterClearValue, ''); + await app.setValue(app.root.form.nameInput, 'testValueNew'); + afterSetValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterSetValue, 'testValueNew'); + await app.clearValue(app.root.form.nameInput); + afterClearValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterClearValue, ''); // placeholder - const nameInputPlaceholder = await api.application.getPlaceHolderValue(api.application.root.form.nameInput); - await api.application.assert.equal(nameInputPlaceholder, 'name'); + const nameInputPlaceholder = await app.getPlaceHolderValue( + app.root.form.nameInput, + ); + await app.assert.equal(nameInputPlaceholder, 'name'); + + // keys + await app.setValue(app.root.form.nameInput, 'testValueKeys'); + afterSetValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterSetValue, 'testValueKeys'); + await app.click(app.root.form.nameInput); + await app.keys(['Control', 'A']); + await app.keys(['Backspace']); + afterClearValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterClearValue, ''); + + // addValue + await app.addValue(app.root.form.nameInput, 'testValueAdd'); + await app.addValue(app.root.form.nameInput, 123); + afterSetValue = await app.getValue(app.root.form.nameInput); + await app.assert.equal(afterSetValue, 'testValueAdd123'); }); diff --git a/packages/e2e-test-app/test/selenium/test/frame.spec.js b/packages/e2e-test-app/test/selenium/test/frame.spec.js new file mode 100644 index 00000000..baacea39 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/frame.spec.js @@ -0,0 +1,54 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'frame.html')); + + let isMainContentVisible = await app.isVisible(app.root.content); + let isFrame1Visible = await app.isVisible(app.root.iframe1); + let isFrame2Visible = await app.isVisible(app.root.iframe2); + let isElementFromIframe1Visible = await app.isVisible(app.root.div1); + let isElementFromIframe2Visible = await app.isVisible(app.root.div2); + + await app.assert.deepEqual( + { + isMainContentVisible, + isFrame1Visible, + isFrame2Visible, + isElementFromIframe1Visible, + isElementFromIframe2Visible + }, + { + isMainContentVisible: true, + isFrame1Visible: true, + isFrame2Visible: true, + isElementFromIframe1Visible: false, + isElementFromIframe2Visible: false + } + ); + + let iframe1ID = await app.execute(() => { + return document.querySelector('[data-test-automation-id="iframe1"]'); + }); + await app.switchToFrame(iframe1ID); + isMainContentVisible = await app.isVisible(app.root.content); + isElementFromIframe1Visible = await app.isVisible(app.root.div1); + await app.assert.deepEqual({ + isMainContentVisible, + isElementFromIframe1Visible + }, { + isMainContentVisible: false, + isElementFromIframe1Visible: true + }); + await app.switchToParentFrame(); + isMainContentVisible = await app.isVisible(app.root.content); + isElementFromIframe1Visible = await app.isVisible(app.root.div1); + await app.assert.deepEqual({ + isMainContentVisible, + isElementFromIframe1Visible + }, { + isMainContentVisible: true, + isElementFromIframe1Visible: false + }); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/get-html-and-texts.spec.js b/packages/e2e-test-app/test/selenium/test/get-html-and-texts.spec.js index b18dfad2..2f198105 100644 --- a/packages/e2e-test-app/test/selenium/test/get-html-and-texts.spec.js +++ b/packages/e2e-test-app/test/selenium/test/get-html-and-texts.spec.js @@ -1,18 +1,28 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; -const expectedHtml = '

test

'; +const expectedHtml = + '

test

'; run(async (api) => { - await api.application.url('http://localhost:8080/html-and-text.html'); - const html = await api.application.getHTML(api.application.root.container); - await api.application.assert.equal(html, expectedHtml); + let app = api.application; + await app.url(getTargetUrl(api, 'html-and-text.html')); + const html = await app.getHTML(app.root.container); + await app.assert.equal(html, expectedHtml); - const text = await api.application.getText(api.application.root.text); - await api.application.assert.equal(text, 'Text is here!'); + const text = await app.getText(app.root.text); + await app.assert.equal(text, 'Text is here!'); - const texts = await api.application.getTexts(api.application.root.texts); - await api.application.assert.deepEqual(texts[0].split('\n'), ['Text 1', 'Text 2', 'Text 3', 'Text 4']); + const texts = await app.getTexts(app.root.texts); + await app.assert.deepEqual(texts[0].split('\n'), [ + 'Text 1', + 'Text 2', + 'Text 3', + 'Text 4', + ]); - const textWithoutFocus = await api.application.getTextWithoutFocus(api.application.root.textWithoutFocus); - await api.application.assert.equal(textWithoutFocus, 'Text without focus'); + const textWithoutFocus = await app.getTextWithoutFocus( + app.root.textWithoutFocus, + ); + await app.assert.equal(textWithoutFocus, 'Text without focus'); }); diff --git a/packages/e2e-test-app/test/selenium/test/get-size.spec.js b/packages/e2e-test-app/test/selenium/test/get-size.spec.js new file mode 100644 index 00000000..7f4b0fba --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/get-size.spec.js @@ -0,0 +1,11 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'get-size.html')); + + let size = await app.getSize(app.root.icon); + + await app.assert.deepEqual(size, { width: 32, height: 32 }); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/get-source.spec.js b/packages/e2e-test-app/test/selenium/test/get-source.spec.js new file mode 100644 index 00000000..967d7feb --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/get-source.spec.js @@ -0,0 +1,11 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'get-source.html')); + + let source = await app.getSource(); + + await app.assert.ok(source.includes('')); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/mock.spec.js b/packages/e2e-test-app/test/selenium/test/mock.spec.js deleted file mode 100644 index f0a0c1e6..00000000 --- a/packages/e2e-test-app/test/selenium/test/mock.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -import {run} from 'testring'; - -run(async (api) => { - const originUrl = 'http://localhost:8080/title.html'; - const mockUrl = 'http://localhost:8080/mock.html'; - - await api.application.client.mock(originUrl, mockUrl); - await api.application.client.mock(mockUrl, function (res) { - res.body = res.body.replace( - '', - '', - ); - }); - await api.application.url(originUrl); - await api.application.assert.isTrue( - await api.application.execute(() => window.__MOCK_INJECT), - ); - await api.application.assert.equal( - await api.application.getTitle(), - 'Mock test', - ); - - const [mockResponse] = await api.application.client.getMockData(mockUrl); - await api.application.assert.include( - mockResponse.body, - '', - ); -}); diff --git a/packages/e2e-test-app/test/selenium/test/screenshots-disabled.spec.js b/packages/e2e-test-app/test/selenium/test/screenshots-disabled.spec.js index f5cb6d0b..d11e1fa3 100644 --- a/packages/e2e-test-app/test/selenium/test/screenshots-disabled.spec.js +++ b/packages/e2e-test-app/test/selenium/test/screenshots-disabled.spec.js @@ -1,11 +1,10 @@ import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/screenshot.html'); - const fName = await api.application.makeScreenshot(); + let app = api.application; + await app.url(getTargetUrl(api, 'screenshot.html')); + const fName = await app.makeScreenshot(); - await api.application.assert.ok( - fName === null, - 'result of stat should be null', - ); -}); \ No newline at end of file + await app.assert.ok(fName === null, 'result of stat should be null'); +}); diff --git a/packages/e2e-test-app/test/selenium/test/scroll-and-move.spec.js b/packages/e2e-test-app/test/selenium/test/scroll-and-move.spec.js index 9eb2eb20..50d8987e 100644 --- a/packages/e2e-test-app/test/selenium/test/scroll-and-move.spec.js +++ b/packages/e2e-test-app/test/selenium/test/scroll-and-move.spec.js @@ -1,9 +1,11 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/scroll.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'scroll.html')); - await api.application.moveToObject(api.application.root.item_10); + await app.moveToObject(app.root.item_10); function getScrollTop(selector) { function getElementByXPath(xpath) { @@ -11,7 +13,8 @@ run(async (api) => { xpath, document, null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, ); if (element.snapshotLength > 0) { @@ -23,36 +26,42 @@ run(async (api) => { return getElementByXPath(selector).scrollTop; } - const scrollTop = await api.application.execute(getScrollTop, api.application.root.container.toString()); - const mouseOverResult10 = await api.application.getText(api.application.root.mouseOverResult); + const scrollTop = await app.execute( + getScrollTop, + app.root.container.toString(), + ); + const mouseOverResult10 = await app.getText(app.root.mouseOverResult); - await api.application.assert.isAtLeast(+scrollTop, 140); - await api.application.assert.equal(mouseOverResult10, '10'); + await app.assert.isAtLeast(+scrollTop, 140); + await app.assert.equal(mouseOverResult10, '10'); - await api.application.scroll(api.application.root.container.item_1); + await app.scroll(app.root.container.item_1); - const scrollTopAfterScrollingToFirstItem = await api.application.getAttribute( - api.application.root.container, 'scrollTop', + const scrollTopAfterScrollingToFirstItem = await app.getAttribute( + app.root.container, + 'scrollTop', ); - await api.application.assert.isAtMost(+scrollTopAfterScrollingToFirstItem, 30); + await app.assert.isAtMost(+scrollTopAfterScrollingToFirstItem, 30); - await api.application.moveToObject(api.application.root.item_1); - const mouseOverResult1 = await api.application.getText(api.application.root.mouseOverResult); - await api.application.assert.equal(mouseOverResult1, '1'); + await app.moveToObject(app.root.item_1); + const mouseOverResult1 = await app.getText(app.root.mouseOverResult); + await app.assert.equal(mouseOverResult1, '1'); - await api.application.scrollIntoView(api.application.root.button); - let scrollTopView = await api.application.execute(() => document.scrollingElement.scrollTop); + await app.scrollIntoView(app.root.button); + let scrollTopView = await app.execute( + () => document.scrollingElement.scrollTop, + ); - await api.application.scrollIntoView(api.application.root.button, -100); - await api.application.assert.equal( - scrollTopView-100, - await api.application.execute(() => document.scrollingElement.scrollTop), + await app.scrollIntoView(app.root.button, -100); + await app.assert.equal( + scrollTopView - 100, + await app.execute(() => document.scrollingElement.scrollTop), ); - await api.application.scrollIntoViewIfNeeded(api.application.root.button); - await api.application.click(api.application.root.button); - await api.application.assert.equal( - scrollTopView-100, - await api.application.execute(() => document.scrollingElement.scrollTop), + await app.scrollIntoViewIfNeeded(app.root.button); + await app.click(app.root.button); + await app.assert.equal( + scrollTopView - 100, + await app.execute(() => document.scrollingElement.scrollTop), ); }); diff --git a/packages/e2e-test-app/test/selenium/test/select.spec.js b/packages/e2e-test-app/test/selenium/test/select.spec.js index 50ba9a5c..afbfd88f 100644 --- a/packages/e2e-test-app/test/selenium/test/select.spec.js +++ b/packages/e2e-test-app/test/selenium/test/select.spec.js @@ -1,36 +1,52 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/form.html'); - - await api.application.selectByValue(api.application.root.form.select, 'byValue'); - const byValueText = await api.application.getSelectedText(api.application.root.form.select); - api.application.assert.equal(byValueText, 'By value'); - - await api.application.selectByAttribute(api.application.root.form.select, 'data-test-attr', 'value'); - const byAttributeText = await api.application.getSelectedText(api.application.root.form.select); - api.application.assert.equal(byAttributeText, 'By attribute'); - - await api.application.selectByIndex(api.application.root.form.select, 2); - const byIndexText = await api.application.getSelectedText(api.application.root.form.select); - api.application.assert.equal(byIndexText, 'By index'); - - await api.application.selectByVisibleText(api.application.root.form.select, 'By visible text'); - const byVisibleText = await api.application.getSelectedText(api.application.root.form.select); - api.application.assert.equal(byVisibleText, 'By visible text'); - - const previousText = await api.application.getSelectedText(api.application.root.form.select); - await api.application.selectNotCurrent(api.application.root.form.select); - const nonCurrentText = await api.application.getSelectedText(api.application.root.form.select); - await api.application.assert.notEqual(nonCurrentText, previousText); - - const selectTexts = await api.application.getSelectTexts(api.application.root.form.select); - const selectValues = await api.application.getSelectValues(api.application.root.form.select); - - await api.application.assert.deepEqual(selectTexts, [ - 'By attribute', 'By name', 'By index', 'By value', 'By visible text', 'With test id', + let app = api.application; + await app.url(getTargetUrl(api, 'form.html')); + + await app.selectByValue(app.root.form.select, 'byValue'); + const byValueText = await app.getSelectedText(app.root.form.select); + await app.assert.equal(byValueText, 'By value'); + + await app.selectByAttribute( + app.root.form.select, + 'data-test-attr', + 'value', + ); + const byAttributeText = await app.getSelectedText(app.root.form.select); + await app.assert.equal(byAttributeText, 'By attribute'); + + await app.selectByIndex(app.root.form.select, 2); + const byIndexText = await app.getSelectedText(app.root.form.select); + await app.assert.equal(byIndexText, 'By index'); + + await app.selectByVisibleText(app.root.form.select, 'By visible text'); + const byVisibleText = await app.getSelectedText(app.root.form.select); + await app.assert.equal(byVisibleText, 'By visible text'); + + const previousText = await app.getSelectedText(app.root.form.select); + await app.selectNotCurrent(app.root.form.select); + const nonCurrentText = await app.getSelectedText(app.root.form.select); + await app.assert.notEqual(nonCurrentText, previousText); + + const selectTexts = await app.getSelectTexts(app.root.form.select); + const selectValues = await app.getSelectValues(app.root.form.select); + + await app.assert.deepEqual(selectTexts, [ + 'By attribute', + 'By name', + 'By index', + 'By value', + 'By visible text', + 'With test id', ]); - await api.application.assert.deepEqual(selectValues, [ - 'byAttribute', 'byName', 'byIndex', 'byValue', 'byVisibleText', 'withTestId', + await app.assert.deepEqual(selectValues, [ + 'byAttribute', + 'byName', + 'byIndex', + 'byValue', + 'byVisibleText', + 'withTestId', ]); }); diff --git a/packages/e2e-test-app/test/selenium/test/selenium-standalone.spec.js b/packages/e2e-test-app/test/selenium/test/selenium-standalone.spec.js new file mode 100644 index 00000000..080eb324 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/selenium-standalone.spec.js @@ -0,0 +1,13 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'form.html')); + + let gridInfo = await app.client.gridTestSession(); + await app.assert.ok(gridInfo.localSelenium); + + let hubInfo = await app.client.getHubConfig(); + await app.assert.ok(hubInfo.localSelenium); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/title.spec.js b/packages/e2e-test-app/test/selenium/test/title.spec.js index 77402f07..9e1bdef0 100644 --- a/packages/e2e-test-app/test/selenium/test/title.spec.js +++ b/packages/e2e-test-app/test/selenium/test/title.spec.js @@ -1,11 +1,13 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/title.html'); - const originalTitle = await api.application.getTitle(); - api.application.assert.equal(originalTitle, 'original title'); + let app = api.application; + await app.url(getTargetUrl(api, 'title.html')); + const originalTitle = await app.getTitle(); + await app.assert.equal(originalTitle, 'original title'); - await api.application.click(api.application.root.changeTitleButton); - const newTitle = await api.application.getTitle(); - api.application.assert.equal(newTitle, 'title changed'); + await app.click(app.root.changeTitleButton); + const newTitle = await app.getTitle(); + await app.assert.equal(newTitle, 'title changed'); }); diff --git a/packages/e2e-test-app/test/selenium/test/upload.spec.js b/packages/e2e-test-app/test/selenium/test/upload.spec.js new file mode 100644 index 00000000..d5e9be84 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/upload.spec.js @@ -0,0 +1,14 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; +import * as path from 'path'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'upload.html')); + + const remoteFilePath = await app.uploadFile(path.join(__dirname, 'fixtures', 'LoremIpsum.pdf')); + await app.setValue(app.root.uploadForm.fileInput, remoteFilePath); + await app.click(app.root.uploadForm.uploadButton); + let isFileUploaded = await app.isBecomeVisible(app.root.successIndicator); + await app.assert.equal(isFileUploaded, true); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/utils.js b/packages/e2e-test-app/test/selenium/test/utils.js new file mode 100644 index 00000000..d6daa7f6 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/utils.js @@ -0,0 +1,10 @@ +export const getTargetUrl = (api, urlPath) => { + let {baseUrl} = api.getEnvironment(); + if (!baseUrl.endsWith('/')) { + baseUrl += '/'; + } + if (urlPath.startsWith('/')) { + urlPath = urlPath.slice(1); + } + return `${baseUrl}${urlPath}`; +}; diff --git a/packages/e2e-test-app/test/selenium/test/wait-for-exist.spec.js b/packages/e2e-test-app/test/selenium/test/wait-for-exist.spec.js index aaa50567..3ed221d6 100644 --- a/packages/e2e-test-app/test/selenium/test/wait-for-exist.spec.js +++ b/packages/e2e-test-app/test/selenium/test/wait-for-exist.spec.js @@ -1,28 +1,32 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - await api.application.url('http://localhost:8080/wait-for-exist.html'); + let app = api.application; + await app.url(getTargetUrl(api, 'wait-for-exist.html')); - await api.application.click(api.application.root.showElement); - await api.application.waitForExist(api.application.root.shouldExist); + await app.click(app.root.showElement); + await app.waitForExist(app.root.shouldExist); - await api.application.waitForNotExists(api.application.root.invalidTestId, 2000); + await app.waitForNotExists(app.root.invalidTestId, 2000); try { - await api.application.waitForExist(api.application.root.invalidTestId, 2000).ifError('Test!'); + await app.waitForExist(app.root.invalidTestId, 2000).ifError('Test!'); } catch (err) { - await api.application.assert.equal(err.message, 'Test!'); + await app.assert.equal(err.message, 'Test!'); } try { - await api.application.waitForExist(api.application.root.invalidTestId, 2000).ifError((err, xpath, timeout) => { - return `Failed to find ${xpath} timeout ${timeout}`; - }); + await app + .waitForExist(app.root.invalidTestId, 2000) + .ifError((err, xpath, timeout) => { + return `Failed to find ${xpath} timeout ${timeout}`; + }); } catch (err) { - await api.application.assert.equal( + await app.assert.equal( err.message, // eslint-disable-next-line max-len - 'Failed to find (//*[@data-test-automation-id=\'root\']//*[@data-test-automation-id=\'invalidTestId\'])[1] timeout 2000', + "Failed to find (//*[@data-test-automation-id='root']//*[@data-test-automation-id='invalidTestId'])[1] timeout 2000", ); } }); diff --git a/packages/e2e-test-app/test/selenium/test/wait-for-visible.spec.js b/packages/e2e-test-app/test/selenium/test/wait-for-visible.spec.js new file mode 100644 index 00000000..0703b255 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/wait-for-visible.spec.js @@ -0,0 +1,27 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'wait-for-visible.html')); + + let isInfoSectionVisible = await app.isVisible(app.root.infoSection.infoText); + + await app.assert.equal(isInfoSectionVisible, false); + + await app.click(app.root.appearButton); + + await app.waitForVisible(app.root.infoSection.infoText); + + isInfoSectionVisible = await app.isVisible(app.root.infoSection.infoText); + + await app.assert.equal(isInfoSectionVisible, true); + + await app.click(app.root.disappearButton); + let isInfoSectionVisibleBefore = await app.isVisible(app.root.infoSection.infoText); + await app.waitForNotVisible(app.root.infoSection.infoText); + let isInfoSectionVisibleAfter = await app.isVisible(app.root.infoSection.infoText); + + await app.assert.equal(isInfoSectionVisibleBefore, true); + await app.assert.equal(isInfoSectionVisibleAfter, false); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/wait-until.spec.js b/packages/e2e-test-app/test/selenium/test/wait-until.spec.js new file mode 100644 index 00000000..5ae8e314 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/wait-until.spec.js @@ -0,0 +1,21 @@ +import {run} from 'testring'; +import {getTargetUrl} from './utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'wait-until.html')); + + let inputValue = await app.getValue(app.root.inputElement); + await app.assert.equal(inputValue, ''); + await app.click(app.root.addInputValueButton); + await app.waitForValue(app.root.inputElement); + inputValue = await app.getValue(app.root.inputElement); + await app.assert.equal(inputValue, 'Input Value'); + + let isOption2Selected = await app.isChecked(app.root.selectElement.option2); + await app.assert.equal(isOption2Selected, false); + await app.click(app.root.addSelectedButton); + await app.waitForSelected(app.root.selectElement.option2); + isOption2Selected = await app.isChecked(app.root.selectElement.option2); + await app.assert.equal(isOption2Selected, true); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/webdriver-protocol/elements.spec.js b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/elements.spec.js new file mode 100644 index 00000000..2cc38144 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/elements.spec.js @@ -0,0 +1,16 @@ +import {run} from 'testring'; +import {getTargetUrl} from '../utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'elements.html')); + + let textareaElement = await app.elements(app.root.textarea); + await app.click(app.root.textarea); + let activeElement = await app.getActiveElement(); + await app.assert.equal(Object.values(activeElement)[0], textareaElement[0].ELEMENT); + + const location = await app.getLocation(app.root.textarea); + await app.assert.equal(typeof location.x, 'number'); + await app.assert.equal(typeof location.y, 'number'); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/webdriver-protocol/save-pdf.spec.js b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/save-pdf.spec.js new file mode 100644 index 00000000..a024ddaa --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/save-pdf.spec.js @@ -0,0 +1,17 @@ +import {run} from 'testring'; +import {getTargetUrl} from '../utils'; +import * as path from 'path'; +import * as fs from 'fs'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'timezone.html')); + + let filepath = path.join(__dirname, 'test.pdf'); + const result = await app.savePDF({ + filepath, + }); + await app.assert.equal(Buffer.isBuffer(result), true); + const isFileExists = fs.existsSync(filepath); + await app.assert.equal(isFileExists, true); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/webdriver-protocol/set-timezone.spec.js b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/set-timezone.spec.js new file mode 100644 index 00000000..a8f44b2b --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/set-timezone.spec.js @@ -0,0 +1,17 @@ +import {run} from 'testring'; +import {getTargetUrl} from '../utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'timezone.html')); + + let currentTimezone = await app.getText(app.root.timezone.value); + + await app.assert.notEqual(currentTimezone, 'Asia/Tokyo'); + + await app.setTimeZone('Asia/Tokyo'); + await app.refresh(); + + let newTimezone = await app.getText(app.root.timezone.value); + await app.assert.equal(newTimezone, 'Asia/Tokyo'); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/webdriver-protocol/status-back-forward.spec.js b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/status-back-forward.spec.js new file mode 100644 index 00000000..fa90e010 --- /dev/null +++ b/packages/e2e-test-app/test/selenium/test/webdriver-protocol/status-back-forward.spec.js @@ -0,0 +1,31 @@ +import {run} from 'testring'; +import {getTargetUrl} from '../utils'; + +run(async (api) => { + let app = api.application; + await app.url(getTargetUrl(api, 'form.html')); + + let status = await app.client.status(); + await app.assert.ok(status.ready); + + let isFormSelectVisible = await app.isVisible(app.root.form.select); + await app.assert.ok(isFormSelectVisible); + + await app.url(getTargetUrl(api, 'get-size.html')); + isFormSelectVisible = await app.isVisible(app.root.form.select); + let isIconVisible = await app.isVisible(app.root.icon); + await app.assert.notOk(isFormSelectVisible); + await app.assert.ok(isIconVisible); + + await app.client.back(); + isFormSelectVisible = await app.isVisible(app.root.form.select); + isIconVisible = await app.isVisible(app.root.icon); + await app.assert.ok(isFormSelectVisible); + await app.assert.notOk(isIconVisible); + + await app.client.forward(); + isFormSelectVisible = await app.isVisible(app.root.form.select); + isIconVisible = await app.isVisible(app.root.icon); + await app.assert.notOk(isFormSelectVisible); + await app.assert.ok(isIconVisible); +}); \ No newline at end of file diff --git a/packages/e2e-test-app/test/selenium/test/windows.spec.js b/packages/e2e-test-app/test/selenium/test/windows.spec.js index 7d3d3679..51b41b10 100644 --- a/packages/e2e-test-app/test/selenium/test/windows.spec.js +++ b/packages/e2e-test-app/test/selenium/test/windows.spec.js @@ -1,96 +1,120 @@ -import { run } from 'testring'; +import {run} from 'testring'; +import {getTargetUrl} from './utils'; run(async (api) => { - let mainTabId = await api.application.getMainTabId(); - await api.application.assert.isString(mainTabId); - await api.application.assert.lengthOf(await api.application.getTabIds(), 1); - - await api.application.openPage('http://localhost:8080/scroll.html'); - await api.application.url('http://localhost:8080/title.html'); - await api.application.assert.equal(mainTabId, await api.application.getCurrentTabId()); - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId]); - - await api.application.newWindow( - 'http://localhost:8080/title.html', - 'first', - {}); - let secondTabId = await api.application.getCurrentTabId(); - await api.application.assert.notEqual(mainTabId, secondTabId); - await api.application.assert.deepEqual( - await api.application.getTabIds(), - [mainTabId, secondTabId]); + let app = api.application; + let mainTabId = await app.getMainTabId(); + await app.assert.isString(mainTabId); + await app.assert.lengthOf(await app.getTabIds(), 1); + + await app.openPage(getTargetUrl(api, 'scroll.html')); + await app.url(getTargetUrl(api, 'title.html')); + await app.maximizeWindow(); + await app.assert.equal(mainTabId, await app.getCurrentTabId()); + await app.assert.deepEqual(await app.getTabIds(), [mainTabId]); + + await app.newWindow(getTargetUrl(api, 'title.html'), 'first', {}); + let secondTabId = await app.getCurrentTabId(); + await app.assert.notEqual(mainTabId, secondTabId); + await app.assert.deepEqual(await app.getTabIds(), [mainTabId, secondTabId]); // Opening url by windowName in same tab - await api.application.newWindow( - 'http://localhost:8080/elements.html', - 'first', - {}); - await api.application.assert.equal(secondTabId, await api.application.getCurrentTabId()); - - - await api.application.newWindow( - 'http://localhost:8080/scroll.html', - 'second', - {}); - let thirdTabId = await api.application.getCurrentTabId(); - await api.application.assert.notEqual(mainTabId, thirdTabId); - await api.application.assert.notEqual(secondTabId, thirdTabId); - await api.application.assert.deepEqual( - await api.application.getTabIds(), - [mainTabId, secondTabId, thirdTabId]); + await app.newWindow(getTargetUrl(api, 'elements.html'), 'first', {}); + await app.assert.equal(secondTabId, await app.getCurrentTabId()); + + await app.newWindow(getTargetUrl(api, 'scroll.html'), 'second', {}); + let thirdTabId = await app.getCurrentTabId(); + await app.assert.notEqual(mainTabId, thirdTabId); + await app.assert.notEqual(secondTabId, thirdTabId); + await app.assert.deepEqual(await app.getTabIds(), [ + mainTabId, + secondTabId, + thirdTabId, + ]); // Switching tabs - await api.application.switchToFirstSiblingTab(); - await api.application.assert.equal(secondTabId, await api.application.getCurrentTabId()); + await app.switchToFirstSiblingTab(); + await app.assert.equal(secondTabId, await app.getCurrentTabId()); - await api.application.switchToMainSiblingTab(thirdTabId); - await api.application.assert.equal(mainTabId, await api.application.getCurrentTabId()); + await app.switchToMainSiblingTab(thirdTabId); + await app.assert.equal(mainTabId, await app.getCurrentTabId()); - await api.application.window(thirdTabId); - await api.application.assert.equal(thirdTabId, await api.application.getCurrentTabId()); + await app.window(thirdTabId); + await app.assert.equal(thirdTabId, await app.getCurrentTabId()); - await api.application.newWindow('http://localhost:8080/title.html'); - await api.application.assert.notEqual(mainTabId, await api.application.getCurrentTabId()); - await api.application.assert.notEqual(secondTabId, await api.application.getCurrentTabId()); - await api.application.assert.notEqual(thirdTabId, await api.application.getCurrentTabId()); - let fourthTabId = await api.application.getCurrentTabId(); + await app.newWindow(getTargetUrl(api, 'title.html')); + await app.assert.notEqual(mainTabId, await app.getCurrentTabId()); + await app.assert.notEqual(secondTabId, await app.getCurrentTabId()); + await app.assert.notEqual(thirdTabId, await app.getCurrentTabId()); + let fourthTabId = await app.getCurrentTabId(); - await api.application.switchTab(thirdTabId); - await api.application.assert.equal(thirdTabId, await api.application.getCurrentTabId()); + await app.switchTab(thirdTabId); + await app.assert.equal(thirdTabId, await app.getCurrentTabId()); // Closing tabs - await api.application.closeFirstSiblingTab(); - await api.application.switchTab(mainTabId); - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId, thirdTabId, fourthTabId]); + await app.closeFirstSiblingTab(); + await app.switchTab(mainTabId); + await app.assert.deepEqual(await app.getTabIds(), [ + mainTabId, + thirdTabId, + fourthTabId, + ]); - await api.application.newWindow('http://localhost:8080/title.html'); - let fifthTabId = await api.application.getCurrentTabId(); + await app.newWindow(getTargetUrl(api, 'title.html')); + let fifthTabId = await app.getCurrentTabId(); - await api.application.switchTab(thirdTabId); - await api.application.closeCurrentTab(); - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId, fourthTabId, fifthTabId]); + await app.switchTab(thirdTabId); + await app.closeCurrentTab(); + await app.assert.deepEqual(await app.getTabIds(), [ + mainTabId, + fourthTabId, + fifthTabId, + ]); - await api.application.closeAllOtherTabs(); - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId]); + await app.closeAllOtherTabs(); + await app.assert.deepEqual(await app.getTabIds(), [mainTabId]); - await api.application.switchTab(mainTabId); - await api.application.assert.equal(mainTabId, await api.application.getCurrentTabId()); + await app.switchTab(mainTabId); + await app.assert.equal(mainTabId, await app.getCurrentTabId()); - await api.application.closeCurrentTab(); + await app.closeCurrentTab(); // Reinit current manager after all tabs are closed - await api.application.openPage('http://localhost:8080/scroll.html'); - mainTabId = await api.application.getCurrentTabId(); - await api.application.newWindow('http://localhost:8080/title.html'); - secondTabId = await api.application.getCurrentTabId(); - await api.application.newWindow('http://localhost:8080/elements.html'); - thirdTabId = await api.application.getCurrentTabId(); - - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId, secondTabId, thirdTabId]); - - await api.application.switchToFirstSiblingTab(); - await api.application.assert.equal(await api.application.getCurrentTabId(), secondTabId); - - await api.application.closeAllOtherTabs(); - await api.application.assert.deepEqual(await api.application.getTabIds(), [mainTabId]); + await app.openPage(getTargetUrl(api, 'scroll.html')); + await app.maximizeWindow(); + mainTabId = await app.getCurrentTabId(); + await app.newWindow(getTargetUrl(api, 'title.html')); + secondTabId = await app.getCurrentTabId(); + await app.newWindow(getTargetUrl(api, 'elements.html')); + thirdTabId = await app.getCurrentTabId(); + + let expectedWindows = [ + mainTabId, + secondTabId, + thirdTabId, + ]; + await app.assert.deepEqual(await app.getTabIds(), expectedWindows); + + let windowHandles = await app.windowHandles(); + + await app.assert.deepEqual(windowHandles, expectedWindows); + + let isChecked = await app.isChecked(app.root.checkbox1); + await app.assert.equal(isChecked, false); + await app.click(app.root.checkbox1); + isChecked = await app.isChecked(app.root.checkbox1); + await app.assert.equal(isChecked, true); + await app.refresh(); + isChecked = await app.isChecked(app.root.checkbox1); + await app.assert.equal(isChecked, false); + + await app.switchToFirstSiblingTab(); + await app.assert.equal(await app.getCurrentTabId(), secondTabId); + + await app.closeAllOtherTabs(); + await app.assert.deepEqual(await app.getTabIds(), [mainTabId]); + + let windowSize = await app.getWindowSize(); + await app.assert.isNumber(windowSize.width); + await app.assert.isNumber(windowSize.height); }); diff --git a/packages/e2e-test-app/test/simple/test-1.spec.js b/packages/e2e-test-app/test/simple/test-1.spec.js index e931867f..6a6818f0 100644 --- a/packages/e2e-test-app/test/simple/test-1.spec.js +++ b/packages/e2e-test-app/test/simple/test-1.spec.js @@ -27,5 +27,3 @@ // api.application.assert.equal(cookies[1].key, 'RCRoutingInfo'); // }); - - diff --git a/packages/e2e-test-app/test/simple/test-2.spec.js b/packages/e2e-test-app/test/simple/test-2.spec.js index 0426388c..ef8fb170 100644 --- a/packages/e2e-test-app/test/simple/test-2.spec.js +++ b/packages/e2e-test-app/test/simple/test-2.spec.js @@ -1,7 +1,6 @@ -import { run } from 'testring'; -import { getEnv } from './test-2.helper'; +import {run} from 'testring'; +import {getEnv} from './test-2.helper'; run((api) => { api.log(getEnv(api)); }); - diff --git a/packages/e2e-test-app/tsconfig.json b/packages/e2e-test-app/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/e2e-test-app/tsconfig.json +++ b/packages/e2e-test-app/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/element-path/src/create-element-path.ts b/packages/element-path/src/create-element-path.ts index 662b7e8a..5a8be834 100644 --- a/packages/element-path/src/create-element-path.ts +++ b/packages/element-path/src/create-element-path.ts @@ -1,4 +1,4 @@ -import {proxify} from './proxify'; +import {proxify, XpathLocatorProxified} from './proxify'; import {ElementPath, FlowsObject} from './element-path'; export type createElementPathOptions = { @@ -6,10 +6,23 @@ export type createElementPathOptions = { strictMode?: boolean; }; +export type ElementPathProxy = ElementPath & { + xpathByLocator: (element: XpathLocatorProxified) => ElementPathProxy; + xpathByElement: (element: XpathLocatorProxified) => ElementPathProxy; + xpath: (id: string, xpath: string) => ElementPathProxy; + __getInstance: () => ElementPath; + __path?: ElementPath['getElementPathChain']; + __findChildren: (options: any) => ElementPathProxy; + __getReversedChain: ElementPath['getReversedChain']; + __getChildType: ElementPath['getElementType']; +} & { + [key: string]: ElementPathProxy; +}; + export function createElementPath(options: createElementPathOptions = {}) { const {strictMode, flows} = options; const obj = new ElementPath({flows}); - return proxify(obj, strictMode); + return proxify(obj, strictMode) as ElementPathProxy; } diff --git a/packages/element-path/src/index.ts b/packages/element-path/src/index.ts index acdad6a4..0686ca3c 100644 --- a/packages/element-path/src/index.ts +++ b/packages/element-path/src/index.ts @@ -1,2 +1,2 @@ export {ElementPath} from './element-path'; -export {createElementPath} from './create-element-path'; +export {createElementPath, ElementPathProxy} from './create-element-path'; diff --git a/packages/element-path/src/proxify.ts b/packages/element-path/src/proxify.ts index 2b3f5112..d33c69c4 100644 --- a/packages/element-path/src/proxify.ts +++ b/packages/element-path/src/proxify.ts @@ -15,10 +15,11 @@ type PropertyDescriptor = } | undefined; -type XpathLocatorProxified = { +export type XpathLocatorProxified = { id: string; locator: string; parent?: string; + jira?: string; }; const PROXY_OWN_PROPS = ['__flows', '__path']; @@ -31,7 +32,7 @@ const PROXY_PROPS = [ ]; export function proxify(instance: ElementPath, strictMode = true) { - const revocable = Proxy.revocable(instance, { + const revocable = Proxy.revocable(instance, { /* eslint-disable no-use-before-define */ get: getTrap, set: setTrap, diff --git a/packages/element-path/test/empty-options/base-methods.spec.ts b/packages/element-path/test/empty-options/base-methods.spec.ts index a0327e3f..1ce4ff62 100644 --- a/packages/element-path/test/empty-options/base-methods.spec.ts +++ b/packages/element-path/test/empty-options/base-methods.spec.ts @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {createElementPath} from '../../src'; +import {createElementPath, ElementPathProxy} from '../../src'; describe('base methods', () => { describe('creation', () => { @@ -18,13 +18,16 @@ describe('base methods', () => { it('set property', () => { const dummy = createElementPath(); - const setter = () => (dummy.test = 123); + const setter = () => + (dummy.test = 123 as unknown as ElementPathProxy); expect(setter).to.throw(TypeError, 'Immutable object'); }); it('set own property', () => { const dummy = createElementPath(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const setter = () => (dummy.__path = 123); expect(setter).to.throw(TypeError, 'Immutable object'); }); diff --git a/packages/element-path/test/empty-options/not-supported-keys.spec.ts b/packages/element-path/test/empty-options/not-supported-keys.spec.ts index 740e1829..62925d46 100644 --- a/packages/element-path/test/empty-options/not-supported-keys.spec.ts +++ b/packages/element-path/test/empty-options/not-supported-keys.spec.ts @@ -6,6 +6,8 @@ describe('not supported keys', () => { it('get [Symbol]', () => { const symbol = Symbol('test'); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore expect(empty[symbol]).to.be.equal(undefined); }); diff --git a/packages/element-path/test/empty-options/own-properties.spec.ts b/packages/element-path/test/empty-options/own-properties.spec.ts index 6de6c1c0..06329fa0 100644 --- a/packages/element-path/test/empty-options/own-properties.spec.ts +++ b/packages/element-path/test/empty-options/own-properties.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -5,6 +6,7 @@ describe('own properties', () => { const empty = createElementPath(); it('.flows is hidden', () => { + // @ts-ignore expect(empty.flows.toString()).to.be.equal( "(//*[@data-test-automation-id='root']//*[@data-test-automation-id='flows'])[1]", ); @@ -17,24 +19,28 @@ describe('own properties', () => { }); it('.attributeName is hidden', () => { + // @ts-ignore expect(empty.attributeName.toString()).to.be.equal( "(//*[@data-test-automation-id='root']//*[@data-test-automation-id='attributeName'])[1]", ); }); it('.searchMask is hidden', () => { + // @ts-ignore expect(empty.searchMask.toString()).to.be.equal( "(//*[@data-test-automation-id='root']//*[@data-test-automation-id='searchMask'])[1]", ); }); it('.searchOptions is hidden', () => { + // @ts-ignore expect(empty.searchOptions.toString()).to.be.equal( "(//*[@data-test-automation-id='root']//*[@data-test-automation-id='searchOptions'])[1]", ); }); it('.parent is hidden', () => { + // @ts-ignore expect(empty.parent.toString()).to.be.equal( "(//*[@data-test-automation-id='root']//*[@data-test-automation-id='parent'])[1]", ); diff --git a/packages/element-path/test/empty-options/query-by-contains-key.spec.ts b/packages/element-path/test/empty-options/query-by-contains-key.spec.ts index c7f5338e..8d38b4f0 100644 --- a/packages/element-path/test/empty-options/query-by-contains-key.spec.ts +++ b/packages/element-path/test/empty-options/query-by-contains-key.spec.ts @@ -57,8 +57,7 @@ describe("empty options ElementPath root['*foo*']", () => { query: { containsKey: 'foo', }, - xpath: - "/descendant::*[contains(@data-test-automation-id,'foo')]", + xpath: "/descendant::*[contains(@data-test-automation-id,'foo')]", }, ]), }); diff --git a/packages/element-path/test/empty-options/query-by-prefix-and-index.spec.ts b/packages/element-path/test/empty-options/query-by-prefix-and-index.spec.ts index 3e89ce40..8aa72944 100644 --- a/packages/element-path/test/empty-options/query-by-prefix-and-index.spec.ts +++ b/packages/element-path/test/empty-options/query-by-prefix-and-index.spec.ts @@ -58,8 +58,7 @@ describe("empty options ElementPath root['foo*'][0]", () => { index: 0, prefix: 'foo', }, - xpath: - "/descendant::*[starts-with(@data-test-automation-id, 'foo')][position() = 1]", + xpath: "/descendant::*[starts-with(@data-test-automation-id, 'foo')][position() = 1]", }, ]), }); diff --git a/packages/element-path/test/empty-options/query-by-prefix.spec.ts b/packages/element-path/test/empty-options/query-by-prefix.spec.ts index 6486f52d..13659aa8 100644 --- a/packages/element-path/test/empty-options/query-by-prefix.spec.ts +++ b/packages/element-path/test/empty-options/query-by-prefix.spec.ts @@ -57,8 +57,7 @@ describe("empty options ElementPath root['foo*']", () => { query: { prefix: 'foo', }, - xpath: - "/descendant::*[starts-with(@data-test-automation-id, 'foo')]", + xpath: "/descendant::*[starts-with(@data-test-automation-id, 'foo')]", }, ]), }); diff --git a/packages/element-path/test/empty-options/query-complex-selector.spec.ts b/packages/element-path/test/empty-options/query-complex-selector.spec.ts index 30493666..2db3b113 100644 --- a/packages/element-path/test/empty-options/query-complex-selector.spec.ts +++ b/packages/element-path/test/empty-options/query-complex-selector.spec.ts @@ -17,16 +17,17 @@ describe('Heavy selectors', () => { }); it('__findChildren call', () => { - const findChildren = root.extensionsSelectorPopup.popupContent.extensionsSelectorGrid.__findChildren( - { - prefix: 'extension', - index: 0, - subQuery: { - exactKey: 'pin', - containsText: '101', + const findChildren = + root.extensionsSelectorPopup.popupContent.extensionsSelectorGrid.__findChildren( + { + prefix: 'extension', + index: 0, + subQuery: { + exactKey: 'pin', + containsText: '101', + }, }, - }, - ); + ); expect(findChildren.toString()).to.be.equal( "(//*[@data-test-automation-id='root']" + @@ -39,16 +40,17 @@ describe('Heavy selectors', () => { }); it('__findChildren call after indexed element', () => { - const findChildren = root.extensionsSelectorPopup.popupContent.extensionsSelectorGrid.row[0].__findChildren( - { - prefix: 'extension', - index: 0, - subQuery: { - exactKey: 'pin', - containsText: '101', + const findChildren = + root.extensionsSelectorPopup.popupContent.extensionsSelectorGrid.row[0].__findChildren( + { + prefix: 'extension', + index: 0, + subQuery: { + exactKey: 'pin', + containsText: '101', + }, }, - }, - ); + ); expect(findChildren.toString()).to.be.equal( "(//*[@data-test-automation-id='root']" + diff --git a/packages/element-path/test/empty-options/query-only-contains-text.spec.ts b/packages/element-path/test/empty-options/query-only-contains-text.spec.ts index 4c6bc5a7..7f70846e 100644 --- a/packages/element-path/test/empty-options/query-only-contains-text.spec.ts +++ b/packages/element-path/test/empty-options/query-only-contains-text.spec.ts @@ -58,8 +58,7 @@ describe("empty options ElementPath root['{Text element}']", () => { anyKey: true, containsText: 'Text element', }, - xpath: - '/descendant::*[@data-test-automation-id and contains(., "Text element")]', + xpath: '/descendant::*[@data-test-automation-id and contains(., "Text element")]', }, ]), }); diff --git a/packages/element-path/test/empty-options/query-only-equals-text.spec.ts b/packages/element-path/test/empty-options/query-only-equals-text.spec.ts index 0235236f..17c44ccc 100644 --- a/packages/element-path/test/empty-options/query-only-equals-text.spec.ts +++ b/packages/element-path/test/empty-options/query-only-equals-text.spec.ts @@ -58,8 +58,7 @@ describe("empty options ElementPath root['={Text element}']", () => { anyKey: true, equalsText: 'Text element', }, - xpath: - '/descendant::*[@data-test-automation-id and . = "Text element"]', + xpath: '/descendant::*[@data-test-automation-id and . = "Text element"]', }, ]), }); diff --git a/packages/element-path/test/flows-option/deep-child-flow.spec.ts b/packages/element-path/test/flows-option/deep-child-flow.spec.ts index f3e917ac..2c7d3669 100644 --- a/packages/element-path/test/flows-option/deep-child-flow.spec.ts +++ b/packages/element-path/test/flows-option/deep-child-flow.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -41,6 +42,7 @@ describe('flows option on deep child', () => { }); it('function call', async () => { + // @ts-ignore expect(await deepChildFoo.foo()).to.be.equal('test string foo'); }); }); @@ -52,6 +54,7 @@ describe('flows option on deep child', () => { }); it('function call', () => { + // @ts-ignore expect(deepChildFoo.bar()).to.be.equal('test string bar'); }); }); diff --git a/packages/element-path/test/flows-option/default-flow.spec.ts b/packages/element-path/test/flows-option/default-flow.spec.ts index ab0be83a..0861e3f5 100644 --- a/packages/element-path/test/flows-option/default-flow.spec.ts +++ b/packages/element-path/test/flows-option/default-flow.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -116,6 +117,7 @@ describe('flows option default behavior', () => { }); it('function call', async () => { + // @ts-ignore expect(await childFoo.runFlow()).to.be.equal('test string'); }); }); diff --git a/packages/element-path/test/flows-option/flow-context.spec.ts b/packages/element-path/test/flows-option/flow-context.spec.ts index 91496f0b..d9a530eb 100644 --- a/packages/element-path/test/flows-option/flow-context.spec.ts +++ b/packages/element-path/test/flows-option/flow-context.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -16,7 +17,9 @@ describe('flows option function context behavior', () => { const childFoo = root.foo; it('Call chidlFoo.getContext()', () => { + // @ts-ignore expect(childFoo.getContext()).to.be.equal(childFoo.__proxy); + // @ts-ignore expect(childFoo.getContext().__getInstance()).to.be.equal( childFoo.__getInstance(), ); @@ -24,12 +27,14 @@ describe('flows option function context behavior', () => { it('Call childFoo.getContext.apply(obj)', () => { const obj = {}; + // @ts-ignore expect(childFoo.getContext.apply(obj)).to.be.equal(obj); }); it('Call getContext()', () => { // eslint-disable-next-line @typescript-eslint/no-shadow const {getContext} = childFoo; + // @ts-ignore expect(getContext()).to.be.equal(undefined); }); }); diff --git a/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator-parent.spec.ts b/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator-parent.spec.ts index d9aaf6e4..5ab204a1 100644 --- a/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator-parent.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator-parent.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -21,6 +22,7 @@ describe('.xpathByLocator() with parent', () => { describe('arguments validation', () => { it('call without xpath', () => { + // @ts-ignore const error = () => root.xpathByLocator({parent: 'foo.bar'}); expect(error).to.throw( 'Invalid options, "locator" string is required', @@ -39,6 +41,7 @@ describe('.xpathByLocator() with parent', () => { }); it('call without id', () => { + // @ts-ignore const child = root.xpathByLocator({ locator: "//*[@class='selected']", parent: 'foo.bar', @@ -57,6 +60,7 @@ describe('.xpathByLocator() with parent', () => { it('call with not string', () => { const child = root.xpathByLocator({ + // @ts-ignore id: 0, locator: "//*[@class='selected']", parent: 'foo.bar', diff --git a/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator.spec.ts b/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator.spec.ts index 9fb11dbc..6e8f79de 100644 --- a/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-root-xpath-by-locator.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -20,6 +21,7 @@ describe('.xpathByLocator() from root', () => { describe('arguments validation', () => { it('call without xpath', () => { + // @ts-ignore const error = () => root.xpathByLocator({}); expect(error).to.throw( 'Invalid options, "locator" string is required', @@ -27,6 +29,7 @@ describe('.xpathByLocator() from root', () => { }); it('call without id', () => { + // @ts-ignore const child = root.xpathByLocator({ locator: "//*[@class='selected']", }); @@ -43,6 +46,7 @@ describe('.xpathByLocator() from root', () => { it('call with not string', () => { const child = root.xpathByLocator({ + // @ts-ignore id: 0, locator: "//*[@class='selected']", }); diff --git a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias-with-chain.spec.ts b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias-with-chain.spec.ts index 86b6c1f5..e2dd720a 100644 --- a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias-with-chain.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias-with-chain.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -21,6 +22,7 @@ describe('foo.xpathByElement()', () => { describe('arguments validation', () => { it('call without xpath and id', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({}); expect(error).to.throw( 'Invalid options, "xpath" string is required', @@ -28,6 +30,7 @@ describe('foo.xpathByElement()', () => { }); it('call without xpath', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({id: 'selected'}); expect(error).to.throw( 'Invalid options, "xpath" string is required', diff --git a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias.spec.ts b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias.spec.ts index a6dc375a..a07fe831 100644 --- a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element-root-alias.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -21,6 +22,7 @@ describe('.xpathByElement()', () => { describe('arguments validation', () => { it('call without xpath and id', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({}); expect(error).to.throw( 'Invalid options, "xpath" string is required', @@ -28,6 +30,7 @@ describe('.xpathByElement()', () => { }); it('call without xpath', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({id: 'selected'}); expect(error).to.throw( 'Invalid options, "xpath" string is required', diff --git a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element.spec.ts b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element.spec.ts index b77bf626..e0f4eb18 100644 --- a/packages/element-path/test/strict-mode-option/disabled-xpath-by-element.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-xpath-by-element.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -21,6 +22,7 @@ describe('.xpathByElement()', () => { describe('arguments validation', () => { it('call without xpath and id', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({}); expect(error).to.throw( 'Invalid options, "xpath" string is required', @@ -28,6 +30,7 @@ describe('.xpathByElement()', () => { }); it('call without xpath', () => { + // @ts-ignore const error = () => root.foo.xpathByElement({id: 'selected'}); expect(error).to.throw( 'Invalid options, "xpath" string is required', diff --git a/packages/element-path/test/strict-mode-option/disabled-xpath.spec.ts b/packages/element-path/test/strict-mode-option/disabled-xpath.spec.ts index 14faaaf5..8f82ba84 100644 --- a/packages/element-path/test/strict-mode-option/disabled-xpath.spec.ts +++ b/packages/element-path/test/strict-mode-option/disabled-xpath.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -20,6 +21,7 @@ describe('.xpath()', () => { describe('arguments validation', () => { it('call without xpath and id', () => { + // @ts-ignore const error = () => root.foo.xpath(); expect(error).to.throw( 'Invalid options, "xpath" string is required', @@ -27,6 +29,7 @@ describe('.xpath()', () => { }); it('call without xpath', () => { + // @ts-ignore const error = () => root.foo.xpath('test'); expect(error).to.throw( 'Invalid options, "xpath" string is required', @@ -34,7 +37,11 @@ describe('.xpath()', () => { }); it('call without id', () => { - const child = root.foo.xpath(undefined, "//*[@class='selected']"); + const child = root.foo.xpath( + // @ts-ignore + undefined, + "//*[@class='selected']", + ); expect(child.toString()).to.be.equal(xpathSelectorCall.toString()); }); @@ -44,7 +51,11 @@ describe('.xpath()', () => { }); it('call with not string', () => { - const child = root.foo.xpath(0, "//*[@class='selected']"); + const child = root.foo.xpath( + // @ts-ignore + 0, + "//*[@class='selected']", + ); expect(child.toString()).to.be.equal(xpathSelectorCall.toString()); }); }); diff --git a/packages/element-path/test/strict-mode-option/enabled.spec.ts b/packages/element-path/test/strict-mode-option/enabled.spec.ts index ca428772..b6f55005 100644 --- a/packages/element-path/test/strict-mode-option/enabled.spec.ts +++ b/packages/element-path/test/strict-mode-option/enabled.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {expect} from 'chai'; import {createElementPath} from '../../src'; @@ -6,6 +7,7 @@ describe('enabled strictMode', () => { describe('xpath getter', () => { it('call', () => { + // @ts-ignore const error = () => empty.xpath('//testerror'); expect(error).to.throw('Can not use xpath query in strict mode'); }); diff --git a/packages/element-path/tsconfig.json b/packages/element-path/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/element-path/tsconfig.json +++ b/packages/element-path/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/http-api/src/abstract-http-client.ts b/packages/http-api/src/abstract-http-client.ts index 440cbe4e..8561660c 100644 --- a/packages/http-api/src/abstract-http-client.ts +++ b/packages/http-api/src/abstract-http-client.ts @@ -19,9 +19,8 @@ const toString = (c) => c.toString(); export abstract class AbstractHttpClient implements IHttpClient { protected abstract broadcast(options: IHttpRequestMessage): void; - protected loggerClient: LoggerClient = loggerClient.withPrefix( - '[http client]', - ); + protected loggerClient: LoggerClient = + loggerClient.withPrefix('[http client]'); private requestQueue: IQueue; diff --git a/packages/http-api/src/http-server.ts b/packages/http-api/src/http-server.ts index 38e72b8a..dd00497d 100644 --- a/packages/http-api/src/http-server.ts +++ b/packages/http-api/src/http-server.ts @@ -16,7 +16,8 @@ type MakeRequest = (request: IHttpRequest) => Promise; export class HttpServer extends PluggableModule - implements IHttpServerController { + implements IHttpServerController +{ private unsubscribeTransport: Function; private logger: LoggerClient = loggerClient.withPrefix('[http-server]'); @@ -117,7 +118,9 @@ export class HttpServer }, ); - this.logger.debug(`Request failed ${errorAfterHook?.name} - ${errorAfterHook?.message}`); + this.logger.debug( + `Request failed ${errorAfterHook?.name} - ${errorAfterHook?.message}`, + ); } }; diff --git a/packages/http-api/tsconfig.json b/packages/http-api/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/http-api/tsconfig.json +++ b/packages/http-api/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/plugin-babel/tsconfig.json b/packages/plugin-babel/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/plugin-babel/tsconfig.json +++ b/packages/plugin-babel/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/plugin-fs-store/tsconfig.json b/packages/plugin-fs-store/tsconfig.json index a88803cd..a3dc3a7c 100644 --- a/packages/plugin-fs-store/tsconfig.json +++ b/packages/plugin-fs-store/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/plugin-selenium-driver/src/plugin/index.ts b/packages/plugin-selenium-driver/src/plugin/index.ts index 55770d8a..14f91415 100644 --- a/packages/plugin-selenium-driver/src/plugin/index.ts +++ b/packages/plugin-selenium-driver/src/plugin/index.ts @@ -1,21 +1,22 @@ import {SeleniumPluginConfig} from '../types'; -import {IBrowserProxyPlugin, WindowFeaturesConfig} from '@testring/types'; +import { + IBrowserProxyPlugin, + SavePdfOptions, + WindowFeaturesConfig, +} from '@testring/types'; import {ChildProcess} from 'child_process'; import {remote} from 'webdriverio'; import * as deepmerge from 'deepmerge'; -import {spawn} from '@testring/child-process'; +import {spawnWithPipes} from '@testring/child-process'; import {loggerClient} from '@testring/logger'; import {getCrxBase64} from '@testring/dwnld-collector-crx'; import {CDPCoverageCollector} from '@nullcc/code-coverage-client'; import type {Cookie} from '@wdio/protocols'; -import type { - ClickOptions, - MockFilterOptions, -} from 'webdriverio'; +import type {ClickOptions, MockFilterOptions} from 'webdriverio'; import type {JsonCompatible} from '@wdio/types'; import type {RespondWithOptions} from 'webdriverio/build/utils/interception/types'; import webdriver from 'webdriver'; @@ -43,7 +44,7 @@ const DEFAULT_CONFIG: SeleniumPluginConfig = { // for local ChromeDriver args: [] as string[], }, - 'wdio:enforceWebDriverClassic': true + 'wdio:enforceWebDriverClassic': true, }, cdpCoverage: false, }; @@ -106,7 +107,8 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { mergedConfig.hostname = mergedConfig.host; } - const googleChromeOptions = mergedConfig.capabilities?.['goog:chromeOptions']; + const googleChromeOptions = + mergedConfig.capabilities?.['goog:chromeOptions']; if (googleChromeOptions?.args?.includes('--headless=new')) { const extensions = googleChromeOptions.extensions; const dowldMonitorCrx = getCrxBase64(); @@ -121,19 +123,21 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { } private initIntervals() { - if (this.config.clientCheckInterval > 0) { - this.clientCheckInterval = setInterval( - () => this.checkClientsTimeout(), - this.config.clientCheckInterval, - ); - } + if (this.config.workerLimit !== 'local') { + if (this.config.clientCheckInterval > 0) { + this.clientCheckInterval = setInterval( + () => this.checkClientsTimeout(), + this.config.clientCheckInterval, + ); + } - process.on('exit', () => { - clearInterval(this.clientCheckInterval as NodeJS.Timeout); - this.stopAllSessions().catch((err) => { - this.logger.error('Clean process exit failed', err); + process.on('exit', () => { + clearInterval(this.clientCheckInterval as NodeJS.Timeout); + this.stopAllSessions().catch((err) => { + this.logger.error('Clean process exit failed', err); + }); }); - }); + } } private stopAllSessions() { @@ -174,7 +178,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { this.logger.debug('Init local selenium server'); try { - this.localSelenium = spawn('java', [ + this.localSelenium = spawnWithPipes('java', [ ...this.getChromeDriverArgs(), '-jar', seleniumJarPath, @@ -421,6 +425,8 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { public async kill() { this.logger.debug('Kill command is called'); + + // Close all browser sessions for (const applicant of this.browserClients.keys()) { try { await this.end(applicant); @@ -429,8 +435,59 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { } } + // If using 'local' mode, stop all active sessions + if (this.config.workerLimit === 'local') { + await this.stopAllSessions(); + } + if (this.localSelenium) { - this.localSelenium.kill(); + // remove listener + if (this.localSelenium.stderr) { + this.localSelenium.stderr.removeAllListeners('data'); + this.localSelenium.stdout?.removeAllListeners(); + } + + // Ensure all pipes are closed + this.localSelenium.stdout?.destroy(); + this.localSelenium.stderr?.destroy(); + this.localSelenium.stdin?.destroy(); + + this.logger.debug( + `Stopping local Selenium server (PID: ${this.localSelenium.pid})`, + ); + + // Try SIGTERM first + this.localSelenium.kill('SIGTERM'); + + // Wait for exit event with a timeout (ensures it does not hang forever) + const waitForExit = new Promise((resolve) => { + this.localSelenium.once('exit', () => { + this.logger.debug('Selenium process exited.'); + resolve(); + }); + }); + + // Force kill if not exiting within 3 seconds + const forceKill = new Promise((resolve) => { + setTimeout(() => { + if (!this.localSelenium.killed) { + this.logger.warn( + `Selenium did not exit in time. Sending SIGKILL.`, + ); + this.localSelenium.kill('SIGKILL'); + } + resolve(); + }, 3000); + }); + + // Wait for either normal exit or force kill + await Promise.race([waitForExit, forceKill]); + + this.localSelenium.removeAllListeners(); + + this.logger.debug( + 'Selenium process and all associated pipes closed.', + ); } } @@ -450,7 +507,9 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { const client = this.getBrowserClient(applicant); const element = await client.$(selector); - return options && Object.keys(options).length > 0 ? element.click(options) : element.click(); + return options && Object.keys(options).length > 0 + ? element.click(options) + : element.click(); } public async getSize(applicant: string, selector: string) { @@ -562,7 +621,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { return client.getTitle(); } - public async clearElement(applicant: string, xpath: string) { + public async clearValue(applicant: string, xpath: string) { await this.createClient(applicant); const client = this.getBrowserClient(applicant); @@ -991,55 +1050,36 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { return selector.selectByAttribute(attribute, value); } - // @deprecated WAT-1872 - public async gridProxyDetails(applicant: string) { + public async gridTestSession(applicant: string) { await this.createClient(applicant); const client = this.getBrowserClient(applicant); if (this.localSelenium) { return { + sessionId: client.sessionId, + host: this.config.host, + port: this.config.port, localSelenium: true, }; } - return client.gridProxyDetails(client.sessionId); + return client.gridTestSession(client.sessionId); } - // @deprecated WAT-1872 - public async gridTestSession(applicant: string) { + public async getHubConfig(applicant: string) { await this.createClient(applicant); const client = this.getBrowserClient(applicant); if (this.localSelenium) { return { + sessionId: client.sessionId, + host: this.config.host, + port: this.config.port, localSelenium: true, }; } - return client.gridTestSession(client.sessionId); - } - - // @deprecated WAT-1872 - public async getGridNodeDetails(applicant: string) { - await this.createClient(applicant); - const client = this.getBrowserClient(applicant); - - const testSession = await this.gridTestSession(applicant); - - if (testSession && !testSession.localSelenium) { - const proxyDetails = await client.gridProxyDetails(applicant); - - delete testSession.msg; - delete testSession.success; - - delete proxyDetails.msg; - delete proxyDetails.success; - delete proxyDetails.id; - - return {...testSession, ...proxyDetails}; - } - - return testSession; + return client.getHubConfig(); } /** @@ -1075,6 +1115,142 @@ export class SeleniumPlugin implements IBrowserProxyPlugin { }, } as any); } + + public async status(applicant: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + return client.status(); + } + + public async back(applicant: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + return client.back(); + } + + public async forward(applicant: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + return client.forward(); + } + + public async getActiveElement(applicant: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + return client.getActiveElement(); + } + + public async getLocation(applicant: string, xpath: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + const element = client.$(xpath); + return element.getLocation(); + } + + public async setTimeZone(applicant: string, timeZone: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + return client.setTimeZone(timeZone); + } + + public async getWindowSize(applicant: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + return client.getWindowSize(); + } + + public async savePDF(applicant: string, options: SavePdfOptions) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const {filepath, ...restOptions} = options; + + return client.savePDF(filepath, restOptions); + } + + public async addValue( + applicant: string, + xpath: string, + value: string | number, + ) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.addValue(value); + } + + public async doubleClick(applicant: string, xpath: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.doubleClick(); + } + + public async isClickable(applicant: string, xpath: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.isClickable(); + } + + public async waitForClickable( + applicant: string, + xpath: string, + timeout: number, + ) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.waitForClickable({timeout}); + } + + public async isFocused(applicant: string, xpath: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.isFocused(); + } + + public async isStable(applicant: string, xpath: string) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.isStable(); + } + + public async waitForEnabled( + applicant: string, + xpath: string, + timeout: number, + ) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.waitForEnabled({timeout}); + } + + public async waitForStable( + applicant: string, + xpath: string, + timeout: number, + ) { + await this.createClient(applicant); + const client = this.getBrowserClient(applicant); + + const selector = await client.$(xpath); + return selector.waitForStable({timeout}); + } } export default function seleniumProxy(config: SeleniumPluginConfig) { diff --git a/packages/plugin-selenium-driver/src/types.ts b/packages/plugin-selenium-driver/src/types.ts index 8d861c63..0ed869de 100644 --- a/packages/plugin-selenium-driver/src/types.ts +++ b/packages/plugin-selenium-driver/src/types.ts @@ -1,4 +1,4 @@ -import { Capabilities } from '@wdio/types'; +import {Capabilities} from '@wdio/types'; export type SeleniumPluginConfig = Capabilities.WebdriverIOConfig & { chromeDriverPath?: string; @@ -8,4 +8,5 @@ export type SeleniumPluginConfig = Capabilities.WebdriverIOConfig & { host?: string; // fallback for configuration. In WebdriverIO 5 field host renamed to hostname desiredCapabilities?: Capabilities.RequestedStandaloneCapabilities[]; // fallback for configuration. In WebdriverIO 5 field renamed cdpCoverage: boolean; + workerLimit?: number | 'local'; }; diff --git a/packages/plugin-selenium-driver/tsconfig.json b/packages/plugin-selenium-driver/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/plugin-selenium-driver/tsconfig.json +++ b/packages/plugin-selenium-driver/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/test-utils/tsconfig.json +++ b/packages/test-utils/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/packages/web-application/src/browser-scripts.ts b/packages/web-application/src/browser-scripts.ts new file mode 100644 index 00000000..b3fc05a1 --- /dev/null +++ b/packages/web-application/src/browser-scripts.ts @@ -0,0 +1,290 @@ +/* eslint-disable */ +export const simulateJSFieldChangeScript = (xpath, value, done) => { + const supportedInputTypes = { + color: true, + date: true, + datetime: true, + 'datetime-local': true, + email: true, + month: true, + number: true, + password: true, + range: true, + search: true, + tel: true, + text: true, + time: true, + url: true, + week: true, + }; + + function isTextNode(el) { + const nodeName = el.nodeName.toLowerCase(); + + return ( + nodeName === 'textarea' || + (nodeName === 'input' && + supportedInputTypes[el.getAttribute('type')]) + ); + } + + function simulateEvent(el, type) { + const oEvent = new CustomEvent(type, { + bubbles: true, + cancelable: true, + }); + + el.dispatchEvent(oEvent); + } + + function simulateKey(el, type, keyCode, key) { + const oEvent = new KeyboardEvent(type, { + bubbles: true, + cancelable: true, + key, + }); + + // Chromium Hack + Object.defineProperty(oEvent, 'keyCode', { + get() { + return this.keyCodeVal; + }, + }); + Object.defineProperty(oEvent, 'which', { + get() { + return this.keyCodeVal; + }, + }); + + (oEvent as any).keyCodeVal = keyCode; + + el.dispatchEvent(oEvent); + } + + function getElementByXPath(xpath) { + const element = document.evaluate( + xpath, + document, + null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, + ); + if (element.snapshotLength > 0) { + return element.snapshotItem(0) as any; + } + + return null; + } + + try { + (function (el) { + if (el) { + el.focus(); + + if (isTextNode(el)) { + simulateKey(el, 'keydown', 8, 'Backspace'); + simulateKey(el, 'keypress', 8, 'Backspace'); + simulateKey(el, 'keyup', 8, 'Backspace'); + } + + el.value = value; + simulateEvent(el, 'input'); + simulateEvent(el, 'change'); + done(); + } else { + throw Error(`Element ${xpath} not found.`); + } + })(getElementByXPath(xpath)); + } catch (e) { + done(`${e.message} ${xpath}`); + } +}; + +export const getOptionsPropertyScript = (xpath, prop, done) => { + function getElementByXPath(xpath) { + const element = document.evaluate( + xpath, + document, + null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, + ); + + if (element.snapshotLength > 0) { + return element.snapshotItem(0) as any; + } + + return null; + } + + try { + const element = getElementByXPath(xpath); + + if (element && element.tagName.toLowerCase() === 'select') { + const texts: Array = []; + + for (let i = 0, len = element.options.length; i < len; i++) { + texts.push(element.options[i][prop]); + } + + done(texts); + } else { + throw Error('Element not found'); + } + } catch (e) { + throw Error(`${e.message} ${xpath}`); + } +}; + +export const scrollIntoViewCallScript = ( + xpath, + topOffset, + leftOffset, + done, +) => { + // eslint-disable-next-line sonarjs/no-identical-functions + function getElementByXPath(xpath) { + const element = document.evaluate( + xpath, + document, + null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, + ); + + if (element.snapshotLength > 0) { + return element.snapshotItem(0) as any; + } + + return null; + } + + function isScrollable(el) { + const hasScrollableContent = el.scrollHeight > el.clientHeight; + + const overflowYStyle = window.getComputedStyle(el).overflowY; + const isOverflowHidden = overflowYStyle.indexOf('hidden') !== -1; + + return hasScrollableContent && !isOverflowHidden; + } + + function getScrollableParent(el) { + // eslint-disable-next-line no-nested-ternary + return !el || el === document.scrollingElement || document.body + ? document.scrollingElement || document.body + : isScrollable(el) + ? el + : getScrollableParent(el.parentNode); + } + + try { + const element = getElementByXPath(xpath); + const parent = getScrollableParent(element); + + if (element) { + element.scrollIntoView(); + parent.scrollBy(leftOffset, topOffset); + setTimeout(done, 200); // Gives browser some time to update values + } else { + done('Element not found'); + } + } catch (err) { + done(`${err.message} ${xpath}`); + } +}; + +export const scrollIntoViewIfNeededCallScript = ( + xpath, + topOffset, + leftOffset, + done, +) => { + // eslint-disable-next-line sonarjs/no-identical-functions + function getElementByXPath(xpath) { + const element = document.evaluate( + xpath, + document, + null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, + ); + + if (element.snapshotLength > 0) { + return element.snapshotItem(0) as any; + } + + return null; + } + + // eslint-disable-next-line sonarjs/no-identical-functions + function isScrollable(el) { + const hasScrollableContent = el.scrollHeight > el.clientHeight; + + const overflowYStyle = window.getComputedStyle(el).overflowY; + const isOverflowHidden = overflowYStyle.indexOf('hidden') !== -1; + + return hasScrollableContent && !isOverflowHidden; + } + + // eslint-disable-next-line sonarjs/no-identical-functions + function getScrollableParent(el) { + // eslint-disable-next-line no-nested-ternary + return !el || el === document.scrollingElement || document.body + ? document.scrollingElement || document.body + : isScrollable(el) + ? el + : getScrollableParent(el.parentNode); + } + + function scrollIntoViewIfNeeded(element, topOffset, leftOffset) { + const parent = element.parentNode; + const scrollableParent = getScrollableParent(element); + const parentComputedStyle = window.getComputedStyle(parent, null); + /* eslint-disable max-len */ + const parentBorderTopWidth = + parseInt(parentComputedStyle.getPropertyValue('border-top-width')) + + topOffset; + const parentBorderLeftWidth = + parseInt( + parentComputedStyle.getPropertyValue('border-left-width'), + ) + leftOffset; + const overTop = element.offsetTop - parent.offsetTop < parent.scrollTop; + const overBottom = + element.offsetTop - + parent.offsetTop + + element.clientHeight - + parentBorderTopWidth > + parent.scrollTop + parent.clientHeight; + const overLeft = + element.offsetLeft - parent.offsetLeft < parent.scrollLeft; + const overRight = + element.offsetLeft - + parent.offsetLeft + + element.clientWidth - + parentBorderLeftWidth > + parent.scrollLeft + parent.clientWidth; + /* eslint-enable max-len */ + + if (overTop || overBottom || overLeft || overRight) { + element.scrollIntoViewIfNeeded(); + scrollableParent.scrollBy(leftOffset, topOffset); + } + } + + try { + const element = getElementByXPath(xpath); + + if (element) { + if (topOffset || leftOffset) { + scrollIntoViewIfNeeded(element, topOffset, leftOffset); + } else { + element.scrollIntoViewIfNeeded(); + } + setTimeout(done, 200); + } else { + throw new Error('Element not found'); + } + } catch (err) { + done(`${err.message} ${xpath}`); + } +}; diff --git a/packages/web-application/src/web-application.ts b/packages/web-application/src/web-application.ts index f252a443..5d767da0 100644 --- a/packages/web-application/src/web-application.ts +++ b/packages/web-application/src/web-application.ts @@ -15,17 +15,24 @@ import { WebApplicationDevtoolCallback, ExtensionPostMessageTypes, FSFileLogType, + SavePdfOptions, } from '@testring/types'; import {asyncBreakpoints} from '@testring/async-breakpoints'; import {loggerClient, LoggerClient} from '@testring/logger'; import {generateUniqId} from '@testring/utils'; import {PluggableModule} from '@testring/pluggable-module'; -import {createElementPath, ElementPath} from '@testring/element-path'; +import {createElementPath, ElementPathProxy} from '@testring/element-path'; import {createAssertion} from '@testring/async-assert'; import {WebClient} from './web-client'; import * as utils from './utils'; +import { + getOptionsPropertyScript, + scrollIntoViewCallScript, + scrollIntoViewIfNeededCallScript, + simulateJSFieldChangeScript, +} from './browser-scripts'; type valueType = string | number | null | undefined; @@ -259,6 +266,47 @@ export class WebApplication extends PluggableModule { selectByAttribute(xpath, attribute: string, value: string) { return `Select by attribute ${attribute} with value ${value} from ${xpath}`; }, + getLocation(xpath) { + return `Get location from ${this.formatXpath(xpath)}`; + }, + getActiveElement() { + return 'Get active element'; + }, + setTimeZone(timezone: string) { + return `Set browser timezone to ${timezone}`; + }, + getWindowSize() { + return 'Get window size'; + }, + savePDF(options: SavePdfOptions) { + return `Save PDF to ${options.filepath}`; + }, + addValue(xpath, value: string | number) { + return `Add value ${value} to ${this.formatXpath(xpath)}`; + }, + doubleClick(xpath) { + return `Double click on ${this.formatXpath(xpath)}`; + }, + waitForClickable(xpath, timeout = this.WAIT_TIMEOUT) { + return `Wait for clickable ${this.formatXpath( + xpath, + )} for ${timeout}`; + }, + isClickable(xpath) { + return `Is clickable ${this.formatXpath(xpath)}`; + }, + isFocused(xpath) { + return `Is focused ${this.formatXpath(xpath)}`; + }, + isStable(xpath) { + return `Is stable ${this.formatXpath(xpath)}`; + }, + waitForEnabled(xpath, timeout = this.WAIT_TIMEOUT) { + return `Wait for enabled ${this.formatXpath(xpath)} for ${timeout}`; + }, + waitForStable(xpath, timeout = this.WAIT_TIMEOUT) { + return `Wait for stable ${this.formatXpath(xpath)} for ${timeout}`; + }, }; constructor( @@ -295,10 +343,8 @@ export class WebApplication extends PluggableModule { } const promiseGetter = (self, logFn, errorFn, originMethod, args) => { - let errorLogInterceptor: ( - err: Error, - ...args: any - ) => string = errorFn; + let errorLogInterceptor: (err: Error, ...args: any) => string = + errorFn; // eslint-disable-next-line no-async-promise-executor const promise = new Promise(async (resolve, reject) => { @@ -519,19 +565,21 @@ export class WebApplication extends PluggableModule { return utils.getFormattedString(xpath); } - protected getRootSelector(): ElementPath { - return this.root; + protected getRootSelector(): ElementPathProxy { + return this.root as ElementPathProxy; } protected normalizeSelector( - selector: string | ElementPath, + selector: string | ElementPathProxy, allowMultipleNodesInResult = false, ): string { if (!selector) { return this.getRootSelector().toString(); } - return (selector as ElementPath).toString(allowMultipleNodesInResult); + return (selector as ElementPathProxy).toString( + allowMultipleNodesInResult, + ); } protected async asyncErrorHandler(error) { @@ -543,7 +591,7 @@ export class WebApplication extends PluggableModule { } protected async devtoolHighlight( - xpath: string | ElementPath | null, + xpath: string | ElementPathProxy | null, multiple = false, ): Promise { const normalizedXPath = @@ -562,8 +610,7 @@ export class WebApplication extends PluggableModule { if (addHighlightXpath) { window.postMessage( { - type: - ExtensionPostMessageTypes.ADD_XPATH_HIGHLIGHT, + type: ExtensionPostMessageTypes.ADD_XPATH_HIGHLIGHT, xpath: addHighlightXpath, }, '*', @@ -803,7 +850,7 @@ export class WebApplication extends PluggableModule { await this.waitForExist(normalizedSelector, timeout); await this.makeScreenshot(); - return this.client.click(normalizedSelector, {button: 'middle'}); + return this.client.click(normalizedSelector, {button: 'left'}); } public async clickCoordinates( @@ -816,8 +863,8 @@ export class WebApplication extends PluggableModule { await this.waitForExist(normalizedSelector, timeout); await this.makeScreenshot(); - let hPos = 1; - let vPos = 1; + let hPos = 0; + let vPos = 0; if (typeof options?.x === 'string' || typeof options?.y === 'string') { const {width, height} = await this.client.getSize( @@ -826,36 +873,36 @@ export class WebApplication extends PluggableModule { switch (options?.x) { case 'left': - hPos = 0; + hPos = -Math.ceil(width / 2) + 1; break; case 'right': - hPos = width; + hPos = Math.ceil(width / 2) - 1; break; case 'center': - hPos = Math.ceil(width / 2); + hPos = 0; break; default: - hPos = 1; + hPos = 0; } switch (options?.y) { case 'top': - vPos = 0; + vPos = -Math.ceil(height / 2) + 1; break; case 'bottom': - vPos = width; + vPos = Math.ceil(height / 2) - 1; break; case 'center': - vPos = Math.ceil(height / 2); + vPos = 0; break; default: - hPos = 1; + vPos = 0; } } @@ -884,106 +931,7 @@ export class WebApplication extends PluggableModule { public async simulateJSFieldChange(xpath, value) { /* eslint-disable object-shorthand, no-var */ const result = await this.client.executeAsync( - (xpath, value, done) => { - var supportedInputTypes = { - color: true, - date: true, - datetime: true, - 'datetime-local': true, - email: true, - month: true, - number: true, - password: true, - range: true, - search: true, - tel: true, - text: true, - time: true, - url: true, - week: true, - }; - - function isTextNode(el) { - var nodeName = el.nodeName.toLowerCase(); - - return ( - nodeName === 'textarea' || - (nodeName === 'input' && - supportedInputTypes[el.getAttribute('type')]) - ); - } - - function simulateEvent(el, type) { - var oEvent = new CustomEvent(type, { - bubbles: true, - cancelable: true, - }); - - el.dispatchEvent(oEvent); - } - - function simulateKey(el, type, keyCode, key) { - var oEvent = new KeyboardEvent(type, { - bubbles: true, - cancelable: true, - key: key, - }); - - // Chromium Hack - Object.defineProperty(oEvent, 'keyCode', { - get: function () { - return this.keyCodeVal; - }, - }); - Object.defineProperty(oEvent, 'which', { - get: function () { - return this.keyCodeVal; - }, - }); - - (oEvent as any).keyCodeVal = keyCode; - - el.dispatchEvent(oEvent); - } - - function getElementByXPath(xpath) { - var element = document.evaluate( - xpath, - document, - null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null, - ); - if (element.snapshotLength > 0) { - return element.snapshotItem(0) as any; - } - - return null; - } - - try { - (function (el) { - if (el) { - el.focus(); - - if (isTextNode(el)) { - simulateKey(el, 'keydown', 8, 'Backspace'); - simulateKey(el, 'keypress', 8, 'Backspace'); - simulateKey(el, 'keyup', 8, 'Backspace'); - } - - el.value = value; - simulateEvent(el, 'input'); - simulateEvent(el, 'change'); - done(); - } else { - throw Error(`Element ${xpath} not found.`); - } - })(getElementByXPath(xpath)); - } catch (e) { - done(`${e.message} ${xpath}`); - } - }, + simulateJSFieldChangeScript, xpath, value, ); @@ -1011,6 +959,12 @@ export class WebApplication extends PluggableModule { return this.client.keys(['Backspace']); } + public async clearValue(xpath, timeout: number = this.WAIT_TIMEOUT) { + await this.waitForExist(xpath, timeout); + const normalizedXpath = this.normalizeSelector(xpath); + return this.client.clearValue(normalizedXpath); + } + public async setValue( xpath, value: valueType, @@ -1102,49 +1056,7 @@ export class WebApplication extends PluggableModule { xpath = this.normalizeSelector(xpath); - return this.client.executeAsync( - function (xpath, prop, done) { - function getElementByXPath(xpath) { - const element = document.evaluate( - xpath, - document, - null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null, - ); - - if (element.snapshotLength > 0) { - return element.snapshotItem(0) as any; - } - - return null; - } - - try { - const element = getElementByXPath(xpath); - - if (element && element.tagName.toLowerCase() === 'select') { - const texts: Array = []; - - for ( - let i = 0, len = element.options.length; - i < len; - i++ - ) { - texts.push(element.options[i][prop]); - } - - done(texts); - } else { - throw Error('Element not found'); - } - } catch (e) { - throw Error(`${e.message} ${xpath}`); - } - }, - xpath, - prop, - ); + return this.client.executeAsync(getOptionsPropertyScript, xpath, prop); } public async getSelectTexts( @@ -1435,62 +1347,7 @@ export class WebApplication extends PluggableModule { if (topOffset || leftOffset) { const result = await this.client.executeAsync( - function (xpath, topOffset, leftOffset, done) { - // eslint-disable-next-line sonarjs/no-identical-functions - function getElementByXPath(xpath) { - const element = document.evaluate( - xpath, - document, - null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null, - ); - - if (element.snapshotLength > 0) { - return element.snapshotItem(0) as any; - } - - return null; - } - - function isScrollable(el) { - const hasScrollableContent = - el.scrollHeight > el.clientHeight; - - const overflowYStyle = window.getComputedStyle(el) - .overflowY; - const isOverflowHidden = - overflowYStyle.indexOf('hidden') !== -1; - - return hasScrollableContent && !isOverflowHidden; - } - - function getScrollableParent(el) { - // eslint-disable-next-line no-nested-ternary - return !el || - el === document.scrollingElement || - document.body - ? document.scrollingElement || document.body - : isScrollable(el) - ? el - : getScrollableParent(el.parentNode); - } - - try { - const element = getElementByXPath(xpath); - const parent = getScrollableParent(element); - - if (element) { - element.scrollIntoView(); - parent.scrollBy(leftOffset, topOffset); - setTimeout(done, 200); // Gives browser some time to update values - } else { - done('Element not found'); - } - } catch (err) { - done(`${err.message} ${xpath}`); - } - }, + scrollIntoViewCallScript, normalizedXpath, topOffset, leftOffset, @@ -1524,119 +1381,7 @@ export class WebApplication extends PluggableModule { const normalizedXpath = this.normalizeSelector(xpath); const result: string = await this.client.executeAsync( - function (xpath, topOffset, leftOffset, done) { - // eslint-disable-next-line sonarjs/no-identical-functions - function getElementByXPath(xpath) { - const element = document.evaluate( - xpath, - document, - null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null, - ); - - if (element.snapshotLength > 0) { - return element.snapshotItem(0) as any; - } - - return null; - } - - // eslint-disable-next-line sonarjs/no-identical-functions - function isScrollable(el) { - const hasScrollableContent = - el.scrollHeight > el.clientHeight; - - const overflowYStyle = window.getComputedStyle(el) - .overflowY; - const isOverflowHidden = - overflowYStyle.indexOf('hidden') !== -1; - - return hasScrollableContent && !isOverflowHidden; - } - - // eslint-disable-next-line sonarjs/no-identical-functions - function getScrollableParent(el) { - // eslint-disable-next-line no-nested-ternary - return !el || - el === document.scrollingElement || - document.body - ? document.scrollingElement || document.body - : isScrollable(el) - ? el - : getScrollableParent(el.parentNode); - } - - function scrollIntoViewIfNeeded( - element, - topOffset, - leftOffset, - ) { - const parent = element.parentNode; - const scrollableParent = getScrollableParent(element); - const parentComputedStyle = window.getComputedStyle( - parent, - null, - ); - /* eslint-disable max-len */ - const parentBorderTopWidth = - parseInt( - parentComputedStyle.getPropertyValue( - 'border-top-width', - ), - ) + topOffset; - const parentBorderLeftWidth = - parseInt( - parentComputedStyle.getPropertyValue( - 'border-left-width', - ), - ) + leftOffset; - const overTop = - element.offsetTop - parent.offsetTop < parent.scrollTop; - const overBottom = - element.offsetTop - - parent.offsetTop + - element.clientHeight - - parentBorderTopWidth > - parent.scrollTop + parent.clientHeight; - const overLeft = - element.offsetLeft - parent.offsetLeft < - parent.scrollLeft; - const overRight = - element.offsetLeft - - parent.offsetLeft + - element.clientWidth - - parentBorderLeftWidth > - parent.scrollLeft + parent.clientWidth; - /* eslint-enable max-len */ - - if (overTop || overBottom || overLeft || overRight) { - element.scrollIntoViewIfNeeded(); - scrollableParent.scrollBy(leftOffset, topOffset); - } - } - - try { - const element = getElementByXPath(xpath); - - if (element) { - if (topOffset || leftOffset) { - scrollIntoViewIfNeeded( - element, - topOffset, - leftOffset, - ); - } else { - element.scrollIntoViewIfNeeded(); - } - setTimeout(done, 200); - } else { - throw new Error('Element not found'); - } - } catch (err) { - done(`${err.message} ${xpath}`); - } - }, + scrollIntoViewIfNeededCallScript, normalizedXpath, topOffset, leftOffset, @@ -1816,7 +1561,7 @@ export class WebApplication extends PluggableModule { xpath = this.normalizeSelector(xpath); - return this.client.getHTML(xpath, true); + return this.client.getHTML(xpath, {prettify: false}); } public async getCurrentTabId() { @@ -1973,23 +1718,24 @@ export class WebApplication extends PluggableModule { private async unregisterAppInDevtool(): Promise { return new Promise((resolve, reject) => { - const removeListener = this.transport.on( - WebApplicationDevtoolActions.unregisterComplete, - // eslint-disable-next-line sonarjs/no-identical-functions - (message) => { - if (message.id === this.applicationId) { - if ( - message.error === null || - message.error === undefined - ) { - resolve(); - } else { - reject(message.error); + const removeListener = + this.transport.on( + WebApplicationDevtoolActions.unregisterComplete, + // eslint-disable-next-line sonarjs/no-identical-functions + (message) => { + if (message.id === this.applicationId) { + if ( + message.error === null || + message.error === undefined + ) { + resolve(); + } else { + reject(message.error); + } + removeListener(); } - removeListener(); - } - }, - ); + }, + ); const payload: IWebApplicationRegisterMessage = { id: this.applicationId, @@ -2160,4 +1906,76 @@ export class WebApplication extends PluggableModule { throw e; } } + + public async getLocation(xpath, timeout: number = this.WAIT_TIMEOUT) { + await this.waitForExist(xpath, timeout); + + xpath = this.normalizeSelector(xpath); + return await this.client.getLocation(xpath); + } + + public async getActiveElement() { + return await this.client.getActiveElement(); + } + + public async setTimeZone(timezone: string) { + return this.client.setTimeZone(timezone); + } + + public async getWindowSize() { + return this.client.getWindowSize(); + } + + public async savePDF(options: SavePdfOptions) { + if (!options.filepath) { + throw new Error('Filepath is not defined'); + } + return this.client.savePDF(options); + } + + public async addValue( + xpath, + value: string | number, + timeout: number = this.WAIT_TIMEOUT, + ) { + await this.waitForExist(xpath, timeout); + xpath = this.normalizeSelector(xpath); + return this.client.addValue(xpath, value); + } + + public async doubleClick(xpath, timeout: number = this.WAIT_TIMEOUT) { + await this.waitForExist(xpath, timeout); + xpath = this.normalizeSelector(xpath); + return this.client.doubleClick(xpath); + } + + public async waitForClickable(xpath, timeout: number = this.WAIT_TIMEOUT) { + xpath = this.normalizeSelector(xpath); + return this.client.waitForClickable(xpath, timeout); + } + + public async isClickable(xpath, timeout: number = this.WAIT_TIMEOUT) { + xpath = this.normalizeSelector(xpath); + return this.client.isClickable(xpath); + } + + public async waitForEnabled(xpath, timeout: number = this.WAIT_TIMEOUT) { + xpath = this.normalizeSelector(xpath); + return this.client.waitForEnabled(xpath, timeout); + } + + public async waitForStable(xpath, timeout: number = this.WAIT_TIMEOUT) { + xpath = this.normalizeSelector(xpath); + return this.client.waitForStable(xpath, timeout); + } + + public async isFocused(xpath) { + xpath = this.normalizeSelector(xpath); + return this.client.isFocused(xpath); + } + + public async isStable(xpath) { + xpath = this.normalizeSelector(xpath); + return this.client.isStable(xpath); + } } diff --git a/packages/web-application/src/web-client.ts b/packages/web-application/src/web-client.ts index 333636da..08897255 100644 --- a/packages/web-application/src/web-client.ts +++ b/packages/web-application/src/web-client.ts @@ -4,6 +4,7 @@ import { IWebApplicationClient, IWebApplicationExecuteMessage, IWebApplicationResponseMessage, + SavePdfOptions, WebApplicationMessageType, WindowFeaturesConfig, } from '@testring/types'; @@ -62,6 +63,10 @@ export class WebClient implements IWebApplicationClient { return this.makeRequest(BrowserProxyActions.refresh, []); } + public getHubConfig() { + return this.makeRequest(BrowserProxyActions.getHubConfig, []); + } + public click(xpath, options?) { return this.makeRequest(BrowserProxyActions.click, [xpath, options]); } @@ -70,11 +75,6 @@ export class WebClient implements IWebApplicationClient { return this.makeRequest(BrowserProxyActions.getSize, [xpath]); } - // @deprecated WAT-1872 - public gridProxyDetails() { - return this.makeRequest(BrowserProxyActions.gridProxyDetails, []); - } - public url(val) { return this.makeRequest(BrowserProxyActions.url, [val]); } @@ -125,19 +125,12 @@ export class WebClient implements IWebApplicationClient { return this.makeRequest(BrowserProxyActions.executeAsync, [fn, args]); } - public timeoutsAsyncScript(timeout, fn) { - return this.makeRequest(BrowserProxyActions.timeoutsAsyncScript, [ - timeout, - fn, - ]); - } - public getTitle() { return this.makeRequest(BrowserProxyActions.getTitle, []); } - public clearElement(xpath) { - return this.makeRequest(BrowserProxyActions.clearElement, [xpath]); + public clearValue(xpath) { + return this.makeRequest(BrowserProxyActions.clearValue, [xpath]); } public keys(value) { @@ -361,12 +354,6 @@ export class WebClient implements IWebApplicationClient { ]); } - // @deprecated WAT-1872 - public getGridNodeDetails() { - return this.makeRequest(BrowserProxyActions.getGridNodeDetails, []); - } - - // @deprecated WAT-1872 public gridTestSession() { return this.makeRequest(BrowserProxyActions.gridTestSession, []); } @@ -405,6 +392,81 @@ export class WebClient implements IWebApplicationClient { } public emulateDevice(deviceName = 'iPhone X') { - return this.makeRequest(BrowserProxyActions.emulateDevice, [deviceName]); + return this.makeRequest(BrowserProxyActions.emulateDevice, [ + deviceName, + ]); + } + + public status() { + return this.makeRequest(BrowserProxyActions.status, []); + } + + public back() { + return this.makeRequest(BrowserProxyActions.back, []); + } + + public forward() { + return this.makeRequest(BrowserProxyActions.forward, []); + } + + public getActiveElement() { + return this.makeRequest(BrowserProxyActions.getActiveElement, []); + } + + public getLocation(xpath: string) { + return this.makeRequest(BrowserProxyActions.getLocation, [xpath]); + } + + public setTimeZone(timeZone: string) { + return this.makeRequest(BrowserProxyActions.setTimeZone, [timeZone]); + } + + public getWindowSize() { + return this.makeRequest(BrowserProxyActions.getWindowSize, []); + } + + public savePDF(options: SavePdfOptions) { + return this.makeRequest(BrowserProxyActions.savePDF, [options]); + } + + public addValue(xpath: string, value: string | number) { + return this.makeRequest(BrowserProxyActions.addValue, [xpath, value]); + } + + public doubleClick(xpath: string) { + return this.makeRequest(BrowserProxyActions.doubleClick, [xpath]); + } + + public isClickable(xpath: string) { + return this.makeRequest(BrowserProxyActions.isClickable, [xpath]); + } + + public waitForClickable(xpath: string, timeout: number) { + return this.makeRequest(BrowserProxyActions.waitForClickable, [ + xpath, + timeout, + ]); + } + + public isFocused(xpath: string) { + return this.makeRequest(BrowserProxyActions.isFocused, [xpath]); + } + + public isStable(xpath: string) { + return this.makeRequest(BrowserProxyActions.isStable, [xpath]); + } + + public waitForEnabled(xpath: string, timeout: number) { + return this.makeRequest(BrowserProxyActions.waitForEnabled, [ + xpath, + timeout, + ]); + } + + public waitForStable(xpath: string, timeout: number) { + return this.makeRequest(BrowserProxyActions.waitForStable, [ + xpath, + timeout, + ]); } } diff --git a/packages/web-application/tsconfig.json b/packages/web-application/tsconfig.json index 762f10f4..3967a8a7 100644 --- a/packages/web-application/tsconfig.json +++ b/packages/web-application/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015", diff --git a/utils/templates/tsconfig.json b/utils/templates/tsconfig.json index c937faa2..42a88120 100644 --- a/utils/templates/tsconfig.json +++ b/utils/templates/tsconfig.json @@ -10,7 +10,7 @@ "noEmitOnError": true, "declaration": false, "outDir": "dist", - "sourceMap": false, + "sourceMap": true, "lib": [ "es5", "es2015",