mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-29 10:29:27 -06:00
Merge pull request #32748 from cypress-io/chore/refactor-socket-and-extension
chore: (feature-branch), convert `@packages/socket` and `@packages/extension` into bundle packages and `@packages/extension` to `vitest`
This commit is contained in:
@@ -1785,7 +1785,7 @@ jobs:
|
||||
source ./scripts/ensure-node.sh
|
||||
yarn lerna run types
|
||||
- sanitize-verify-and-store-mocha-results:
|
||||
expectedResultCount: 4
|
||||
expectedResultCount: 3
|
||||
|
||||
verify-release-readiness:
|
||||
<<: *defaults
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
|
||||
#### Notes
|
||||
|
||||
When migrating some of these projects away from the `ts-node` entry [see `@packages/scaffold-config` example](https://github.com/cypress-io/cypress/blob/v15.2.0/packages/scaffold-config/index.js), it is somewhat difficult to make separate browser/node entries as the v8-snapshot [tsconfig.json](https://github.com/cypress-io/cypress/blob/v15.2.0/tooling/v8-snapshot/tsconfig.json) is using an older style of module resolution where the `exports` key inside a package's `package.json` is not well supported. Because of this, we need to find ways to bundle code that is needed internally in the browser vs in node without them being a part of the same bundle. This is a temporary work around until we are able to get every package being able to build as an ES Module, which as that point we can re assess how the Cypress binary is being built as well as v8-snapshots, and will allow us to reconfigure this packages to export content in a more proper fashion.
|
||||
When migrating some of these projects away from the `ts-node` entry [see `@packages/scaffold-config` example](https://github.com/cypress-io/cypress/blob/v15.2.0/packages/scaffold-config/index.js), it is somewhat difficult to make separate browser/node entries as the v8-snapshot [tsconfig.json](https://github.com/cypress-io/cypress/blob/v15.2.0/tooling/v8-snapshot/tsconfig.json) is using an older style of module resolution where the `exports` key inside a package's `package.json` is not well supported. Because of this, we need to find ways to bundle code that is needed internally in the browser vs in node without them being a part of the same bundle. This is a temporary work around until we are able to get every package being able to build as an ES Module, which as that point we can re assess how the Cypress binary is being built as well as v8-snapshots, and will allow us to reconfigure this packages to export content in a more proper fashion. We are currently doing something similar in the following packages:
|
||||
|
||||
* `@packages/scaffold-config`
|
||||
* `@packages/socket`
|
||||
* `@packages/telemetry`
|
||||
|
||||
#### Status
|
||||
|
||||
@@ -44,7 +48,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
|
||||
- [x] packages/error ✅ **COMPLETED**
|
||||
- [x] packages/eslint-config ✅ **COMPLETED**
|
||||
- [ ] packages/example
|
||||
- [ ] packages/extension
|
||||
- [x] packages/extension ✅ **COMPLETED**
|
||||
- [ ] packages/frontend-shared **PARTIAL** - entry point is JS
|
||||
- [x] packages/electron ✅ **COMPLETED**
|
||||
- [x] packages/https-proxy - ✅ **COMPLETED**
|
||||
@@ -63,7 +67,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
|
||||
- [x] packages/runner ✅ **COMPLETED**
|
||||
- [x] packages/scaffold-config ✅ **COMPLETED**
|
||||
- [ ] packages/server **PARTIAL** - many source/test files in JS. highest priority
|
||||
- [ ] packages/socket **PARTIAL** - entry point is JS. Tests are JS
|
||||
- [x] packages/socket ✅ **COMPLETED**
|
||||
- [x] packages/stderr-filtering ✅ **COMPLETED**
|
||||
- [x] packages/telemetry ✅ **COMPLETED**
|
||||
- [ ] packages/ts **PARTIAL** - ultimate goal is removal and likely not worth the effort to convert
|
||||
@@ -95,7 +99,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
|
||||
- [x] packages/driver ✅ **COMPLETED**
|
||||
- [x] packages/electron ✅ **COMPLETED**
|
||||
- [x] packages/error ✅ **COMPLETED**
|
||||
- [ ] packages/extension
|
||||
- [x] packages/extension ✅ **COMPLETED**
|
||||
- [x] packages/https-proxy ✅ **COMPLETED**
|
||||
- [x] packages/electron ✅ **COMPLETED**
|
||||
- [x] packages/icons ✅ **COMPLETED**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AutIframe } from '../../../src/runner/aut-iframe'
|
||||
import { EventManager } from '../../../src/runner/event-manager'
|
||||
import type { Socket } from '@packages/socket/lib/browser'
|
||||
import type { Socket } from '@packages/socket/browser/client'
|
||||
|
||||
export const StubWebsocket = new Proxy<Socket>(Object.create(null), {
|
||||
get: (obj, prop) => {
|
||||
|
||||
2
packages/app/index.d.ts
vendored
2
packages/app/index.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference path="../driver/types/internal-types-lite.d.ts" />
|
||||
|
||||
import type { SocketShape } from '@packages/socket/lib/types'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import type MobX from 'mobx'
|
||||
import type { EventManager } from './src/runner/event-manager'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { LocalBusEmitsMap, LocalBusEventMap, DriverToLocalBus, SocketToDriv
|
||||
import type { RunState, CachedTestState, AutomationElementId, FileDetails, ReporterStartInfo, ReporterRunState } from '@packages/types'
|
||||
|
||||
import { logger } from './logger'
|
||||
import type { SocketShape } from '@packages/socket/lib/types'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import { automation, useRunnerUiStore, useSpecStore } from '../store'
|
||||
import { useScreenshotStore } from '../store/screenshot-store'
|
||||
import { EntrySource, useStudioStore } from '../store/studio-store'
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getRunnerElement, empty } from './utils'
|
||||
import { IframeModel } from './iframe-model'
|
||||
import { AutIframe } from './aut-iframe'
|
||||
import { EventManager } from './event-manager'
|
||||
import { createWebsocket as createWebsocketIo } from '@packages/socket/lib/browser'
|
||||
import { createWebsocket as createWebsocketIo } from '@packages/socket/browser/client'
|
||||
import type { AutomationElementId } from '@packages/types'
|
||||
import { useSnapshotStore } from './snapshot-store'
|
||||
import { useStudioStore } from '../store/studio-store'
|
||||
|
||||
@@ -2,9 +2,8 @@ import util from 'util'
|
||||
|
||||
import type { AddressInfo } from 'net'
|
||||
import type { Server } from 'http'
|
||||
import type { SocketIONamespace, SocketIOServer } from '@packages/socket'
|
||||
import type { SocketIONamespace, SocketIOServer, CDPSocketServer } from '@packages/socket'
|
||||
import type { DataContext } from '..'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
|
||||
export class ServersActions {
|
||||
constructor (private ctx: DataContext) {}
|
||||
|
||||
@@ -4,12 +4,11 @@ import type { NexusGenObjects } from '../gen/nxs.gen'
|
||||
// tslint:disable-next-line no-implicit-dependencies - electron dep needs to be defined
|
||||
import type { App, BrowserWindow } from 'electron'
|
||||
import type { ChildProcess } from 'child_process'
|
||||
import type { SocketIONamespace, SocketIOServer } from '@packages/socket'
|
||||
import type { SocketIONamespace, SocketIOServer, CDPSocketServer } from '@packages/socket'
|
||||
import type { Server } from 'http'
|
||||
import type { ErrorWrapperSource } from '@packages/errors'
|
||||
import type { EventCollectorSource, GitDataSource } from '../sources'
|
||||
import { machineId as getMachineId } from 'node-machine-id'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
|
||||
export type Maybe<T> = T | null | undefined
|
||||
|
||||
|
||||
2
packages/extension/.gitignore
vendored
Normal file
2
packages/extension/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
app-dist/
|
||||
lib-dist/
|
||||
@@ -6,12 +6,20 @@ This is the WebExtension responsible for automating the browser
|
||||
|
||||
### Watching
|
||||
|
||||
Kicks off the gulp watcher that rebuilds the app/lib directories on change.
|
||||
|
||||
```bash
|
||||
yarn workspace @packages/extension watch
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
`@packages/extension` has a few different build processes occurring that are all driven by the [`gulpfile`](./gulpfile.ts).
|
||||
* `app` - The web extension piece of the code, has two separate bundles:
|
||||
* `v2`: Version 2 of the web extension which uses [webpack](./webpack.config.mjs) to bundle the `app/v2` directory and output it as `background.js`
|
||||
* `v3`: Version 3 of the web extension, which doesn't have any external dependencies so we are able to compile down to `ESM to run in the browser natively.
|
||||
* `lib` - the `@packages/extension` `main` entry that has utility methods on how to find/load the extension. This is transpiled to CommonJS as it is consumed in the Node context.
|
||||
|
||||
```bash
|
||||
yarn workspace @packages/extension build
|
||||
```
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const get = require('lodash/get')
|
||||
const once = require('lodash/once')
|
||||
const Promise = require('bluebird')
|
||||
const browser = require('webextension-polyfill')
|
||||
import get from 'lodash/get'
|
||||
import once from 'lodash/once'
|
||||
import Bluebird from 'bluebird'
|
||||
import browser from 'webextension-polyfill'
|
||||
|
||||
const client = require('./client')
|
||||
import { connect as clientConnect } from './client'
|
||||
|
||||
const checkIfFirefox = async () => {
|
||||
if (!browser || !get(browser, 'runtime.getBrowserInfo')) {
|
||||
@@ -15,9 +15,9 @@ const checkIfFirefox = async () => {
|
||||
return name === 'Firefox'
|
||||
}
|
||||
|
||||
const connect = function (host, path, extraOpts) {
|
||||
const connect = function (host: string, path: string, extraOpts?: any) {
|
||||
const listenToCookieChanges = once(() => {
|
||||
return browser.cookies.onChanged.addListener((info) => {
|
||||
return browser.cookies.onChanged.addListener((info: any) => {
|
||||
if (info.cause !== 'overwrite') {
|
||||
return ws.emit('automation:push:request', 'change:cookie', info)
|
||||
}
|
||||
@@ -25,7 +25,7 @@ const connect = function (host, path, extraOpts) {
|
||||
})
|
||||
|
||||
const listenToDownloads = once(() => {
|
||||
browser.downloads.onCreated.addListener((downloadItem) => {
|
||||
browser.downloads.onCreated.addListener((downloadItem: any) => {
|
||||
ws.emit('automation:push:request', 'create:download', {
|
||||
id: `${downloadItem.id}`,
|
||||
filePath: downloadItem.filename,
|
||||
@@ -34,7 +34,7 @@ const connect = function (host, path, extraOpts) {
|
||||
})
|
||||
})
|
||||
|
||||
browser.downloads.onChanged.addListener((downloadDelta) => {
|
||||
browser.downloads.onChanged.addListener((downloadDelta: any) => {
|
||||
const state = (downloadDelta.state || {}).current
|
||||
|
||||
if (state === 'complete') {
|
||||
@@ -51,7 +51,7 @@ const connect = function (host, path, extraOpts) {
|
||||
})
|
||||
})
|
||||
|
||||
const fail = (id, err) => {
|
||||
const fail = (id: number, err: any) => {
|
||||
return ws.emit('automation:response', id, {
|
||||
__error: err.message,
|
||||
__stack: err.stack,
|
||||
@@ -59,21 +59,22 @@ const connect = function (host, path, extraOpts) {
|
||||
})
|
||||
}
|
||||
|
||||
const invoke = function (method, id, ...args) {
|
||||
const respond = (data) => {
|
||||
const invoke = function (method: string, id: number, ...args: any[]) {
|
||||
const respond = (data: any) => {
|
||||
return ws.emit('automation:response', id, { response: data })
|
||||
}
|
||||
|
||||
return Promise.try(() => {
|
||||
return Bluebird.try(() => {
|
||||
// @ts-expect-error
|
||||
return automation[method].apply(automation, args.concat(respond))
|
||||
}).catch((err) => {
|
||||
return fail(id, err)
|
||||
})
|
||||
}
|
||||
|
||||
const ws = client.connect(host, path, extraOpts)
|
||||
const ws = clientConnect(host, path, extraOpts)
|
||||
|
||||
ws.on('automation:request', (id, msg, data) => {
|
||||
ws.on('automation:request', (id: number, msg: string, data: any) => {
|
||||
switch (msg) {
|
||||
case 'reset:browser:state':
|
||||
return invoke('resetBrowserState', id)
|
||||
@@ -82,7 +83,7 @@ const connect = function (host, path, extraOpts) {
|
||||
}
|
||||
})
|
||||
|
||||
ws.on('automation:config', async (config) => {
|
||||
ws.on('automation:config', async (config: any) => {
|
||||
const isFirefox = await checkIfFirefox()
|
||||
|
||||
listenToCookieChanges()
|
||||
@@ -99,15 +100,13 @@ const connect = function (host, path, extraOpts) {
|
||||
return ws
|
||||
}
|
||||
|
||||
const automation = {
|
||||
export const automation = {
|
||||
connect,
|
||||
|
||||
resetBrowserState (fn) {
|
||||
resetBrowserState (fn: any) {
|
||||
// We remove browser data. Firefox goes through this path, while chrome goes through cdp automation
|
||||
// Note that firefox does not support fileSystems or serverBoundCertificates
|
||||
// (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browsingData/DataTypeSet).
|
||||
return browser.browsingData.remove({}, { cache: true, cookies: true, downloads: true, formData: true, history: true, indexedDB: true, localStorage: true, passwords: true, pluginData: true, serviceWorkers: true }).then(fn)
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = automation
|
||||
@@ -1,6 +1,6 @@
|
||||
import { client } from '@packages/socket/lib/browser'
|
||||
import { client } from '@packages/socket/browser/client'
|
||||
|
||||
export const connect = (host, path, extraOpts = {}) => {
|
||||
export const connect = (host: string, path: string, extraOpts: any = {}) => {
|
||||
return client(host, {
|
||||
path,
|
||||
transports: ['websocket'],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const background = require('./background')
|
||||
import { automation } from './background'
|
||||
|
||||
const HOST = 'CHANGE_ME_HOST'
|
||||
const PATH = 'CHANGE_ME_PATH'
|
||||
|
||||
// immediately connect
|
||||
background.connect(HOST, PATH)
|
||||
automation.connect(HOST, PATH)
|
||||
@@ -26,7 +26,7 @@ window.addEventListener('message', ({ data, source }) => {
|
||||
})
|
||||
|
||||
// this listens for messages from the background service worker script
|
||||
port.onMessage.addListener(({ message }) => {
|
||||
port.onMessage.addListener(({ message }: { message: string }) => {
|
||||
// this lets us know the message we sent to the background script to activate
|
||||
// the main tab was successful, so we in turn send it on to Cypress
|
||||
// via postMessage
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global chrome */
|
||||
declare let chrome: any
|
||||
|
||||
// this background script runs in a service worker. it has access to the
|
||||
// extension API, but not direct access the web page or anything else
|
||||
@@ -9,10 +9,9 @@
|
||||
// go to `chrome://extensions` and hit the reload button under the Cypress
|
||||
// extension. sometimes that doesn't work and requires re-launching Chrome
|
||||
// and then reloading the extension via `chrome://extensions`
|
||||
|
||||
async function getFromStorage (key) {
|
||||
async function getFromStorage (key: string) {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(key, (storage) => {
|
||||
chrome.storage.local.get(key, (storage: any) => {
|
||||
resolve(storage[key])
|
||||
})
|
||||
})
|
||||
@@ -23,7 +22,7 @@ async function activateMainTab () {
|
||||
const url = await getFromStorage('mostRecentUrl')
|
||||
const tabs = await chrome.tabs.query({})
|
||||
|
||||
const cypressTab = tabs.find((tab) => tab.url.includes(url))
|
||||
const cypressTab = tabs.find((tab: any) => tab.url.includes(url))
|
||||
|
||||
if (!cypressTab) return
|
||||
|
||||
@@ -41,8 +40,8 @@ async function activateMainTab () {
|
||||
|
||||
// here we connect to the content script, which has access to the web page
|
||||
// running Cypress, but not the extension API
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
port.onMessage.addListener(async ({ message, url }) => {
|
||||
chrome.runtime.onConnect.addListener((port: any) => {
|
||||
port.onMessage.addListener(async ({ message, url }: { message: string, url: string }) => {
|
||||
if (message === 'activate:main:tab') {
|
||||
await activateMainTab()
|
||||
|
||||
@@ -1,87 +1,82 @@
|
||||
import { promisify } from 'util'
|
||||
import { exec } from 'child_process'
|
||||
import gulp from 'gulp'
|
||||
import { rimraf } from 'rimraf'
|
||||
import { waitUntilIconsBuilt } from '../../scripts/ensure-icons'
|
||||
import cp from 'child_process'
|
||||
import * as path from 'path'
|
||||
import { getPathToIcon, getPathToLogo } from '@packages/icons'
|
||||
|
||||
const nodeWebpack = path.join(__dirname, '..', '..', 'scripts', 'run-webpack.js')
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
async function cypressIcons () {
|
||||
await waitUntilIconsBuilt()
|
||||
export async function clean (): Promise<boolean> {
|
||||
const removedAppDist = await rimraf('app-dist')
|
||||
const removedLibDist = await rimraf('lib-dist')
|
||||
|
||||
return require('@packages/icons')
|
||||
}
|
||||
|
||||
function clean (): Promise<boolean> {
|
||||
return rimraf('dist')
|
||||
return removedAppDist && removedLibDist
|
||||
}
|
||||
|
||||
const manifest = (v: 'v2' | 'v3') => {
|
||||
return () => {
|
||||
return gulp.src(`app/${v}/manifest.json`)
|
||||
.pipe(gulp.dest(`dist/${v}`))
|
||||
.pipe(gulp.dest(`app-dist/${v}`))
|
||||
}
|
||||
}
|
||||
|
||||
const background = (cb) => {
|
||||
cp.fork(nodeWebpack, { stdio: 'inherit' }).on('exit', (code) => {
|
||||
cb(code === 0 ? null : new Error(`Webpack process exited with code ${code}`))
|
||||
})
|
||||
const buildAppV2 = async () => {
|
||||
await execAsync('yarn build:v2')
|
||||
}
|
||||
|
||||
const copyScriptsForV3 = () => {
|
||||
return gulp.src('app/v3/*.js')
|
||||
.pipe(gulp.dest('dist/v3'))
|
||||
const buildAppV3 = async () => {
|
||||
await execAsync('yarn build:v3')
|
||||
}
|
||||
|
||||
const buildLib = async () => {
|
||||
await execAsync('yarn build:lib')
|
||||
}
|
||||
|
||||
const html = () => {
|
||||
return gulp.src('app/**/*.html')
|
||||
.pipe(gulp.dest('dist/v2'))
|
||||
.pipe(gulp.dest('dist/v3'))
|
||||
.pipe(gulp.dest('app-dist/v2'))
|
||||
.pipe(gulp.dest('app-dist/v3'))
|
||||
}
|
||||
|
||||
const css = () => {
|
||||
return gulp.src('app/**/*.css')
|
||||
.pipe(gulp.dest('dist/v2'))
|
||||
.pipe(gulp.dest('dist/v3'))
|
||||
.pipe(gulp.dest('app-dist/v2'))
|
||||
.pipe(gulp.dest('app-dist/v3'))
|
||||
}
|
||||
|
||||
const icons = async () => {
|
||||
const cyIcons = await cypressIcons()
|
||||
|
||||
return gulp.src([
|
||||
cyIcons.getPathToIcon('icon_16x16.png'),
|
||||
cyIcons.getPathToIcon('icon_19x19.png'),
|
||||
cyIcons.getPathToIcon('icon_38x38.png'),
|
||||
cyIcons.getPathToIcon('icon_48x48.png'),
|
||||
cyIcons.getPathToIcon('icon_128x128.png'),
|
||||
getPathToIcon('icon_16x16.png'),
|
||||
getPathToIcon('icon_19x19.png'),
|
||||
getPathToIcon('icon_38x38.png'),
|
||||
getPathToIcon('icon_48x48.png'),
|
||||
getPathToIcon('icon_128x128.png'),
|
||||
])
|
||||
.pipe(gulp.dest('dist/v2/icons'))
|
||||
.pipe(gulp.dest('dist/v3/icons'))
|
||||
.pipe(gulp.dest('app-dist/v2/icons'))
|
||||
.pipe(gulp.dest('app-dist/v3/icons'))
|
||||
}
|
||||
|
||||
const logos = async () => {
|
||||
const cyIcons = await cypressIcons()
|
||||
|
||||
// appease TS
|
||||
return gulp.src([
|
||||
cyIcons.getPathToLogo('cypress-bw.png'),
|
||||
getPathToLogo('cypress-bw.png'),
|
||||
])
|
||||
.pipe(gulp.dest('dist/v2/logos'))
|
||||
.pipe(gulp.dest('dist/v3/logos'))
|
||||
.pipe(gulp.dest('app-dist/v2/logos'))
|
||||
.pipe(gulp.dest('app-dist/v3/logos'))
|
||||
}
|
||||
|
||||
const build = gulp.series(
|
||||
export const build = gulp.series(
|
||||
clean,
|
||||
buildAppV2,
|
||||
buildAppV3,
|
||||
gulp.parallel(
|
||||
icons,
|
||||
logos,
|
||||
manifest('v2'),
|
||||
manifest('v3'),
|
||||
background,
|
||||
copyScriptsForV3,
|
||||
html,
|
||||
css,
|
||||
buildLib,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -89,10 +84,4 @@ const watchBuild = () => {
|
||||
return gulp.watch('app/**/*', build)
|
||||
}
|
||||
|
||||
const watch = gulp.series(build, watchBuild)
|
||||
|
||||
module.exports = {
|
||||
build,
|
||||
clean,
|
||||
watch,
|
||||
}
|
||||
export const watch = gulp.series(build, watchBuild)
|
||||
|
||||
5
packages/extension/index.d.ts
vendored
5
packages/extension/index.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
/// <reference path="../../cli/types/jquery/index.d.ts" />
|
||||
|
||||
declare const lib: typeof import('./lib/extension')
|
||||
|
||||
export default lib
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('./lib/extension')
|
||||
@@ -1,38 +0,0 @@
|
||||
const path = require('path')
|
||||
const Promise = require('bluebird')
|
||||
const { getCookieUrl } = require('./util')
|
||||
const fs = Promise.promisifyAll(require('fs'))
|
||||
|
||||
module.exports = {
|
||||
getPathToExtension (...args) {
|
||||
args = [__dirname, '..', 'dist', 'v2'].concat(args)
|
||||
|
||||
return path.join.apply(path, args)
|
||||
},
|
||||
|
||||
getPathToV3Extension (...args) {
|
||||
return path.join(...[__dirname, '..', 'dist', 'v3', ...args])
|
||||
},
|
||||
|
||||
getPathToTheme () {
|
||||
return path.join(__dirname, '..', 'theme')
|
||||
},
|
||||
|
||||
getPathToRoot () {
|
||||
return path.join(__dirname, '..')
|
||||
},
|
||||
|
||||
setHostAndPath (host, path) {
|
||||
const src = this.getPathToExtension('background.js')
|
||||
|
||||
return fs.readFileAsync(src, 'utf8')
|
||||
.then((str) => {
|
||||
return str
|
||||
.replace('CHANGE_ME_HOST', host)
|
||||
.replace('CHANGE_ME_PATH', path)
|
||||
})
|
||||
},
|
||||
|
||||
getCookieUrl,
|
||||
|
||||
}
|
||||
30
packages/extension/lib/index.ts
Normal file
30
packages/extension/lib/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import path from 'path'
|
||||
import { readFile } from 'fs/promises'
|
||||
|
||||
export const getPathToExtension = (...args: string[]) => {
|
||||
args = [__dirname, '..', 'app-dist', 'v2'].concat(args)
|
||||
|
||||
return path.join.apply(path, args)
|
||||
}
|
||||
|
||||
export const getPathToV3Extension = (...args: string[]) => {
|
||||
return path.join(...[__dirname, '..', 'app-dist', 'v3', ...args])
|
||||
}
|
||||
|
||||
export const getPathToTheme = () => {
|
||||
return path.join(__dirname, '..', 'theme')
|
||||
}
|
||||
|
||||
export const getPathToRoot = () => {
|
||||
return path.join(__dirname, '..')
|
||||
}
|
||||
|
||||
export const setHostAndPath = async (host: string, path: string) => {
|
||||
const src = getPathToExtension('background.js')
|
||||
|
||||
const str = await readFile(src, 'utf8')
|
||||
|
||||
return str
|
||||
.replace('CHANGE_ME_HOST', host)
|
||||
.replace('CHANGE_ME_PATH', path)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
getCookieUrl: (cookie = {}) => {
|
||||
const prefix = cookie.secure ? 'https://' : 'http://'
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/6375
|
||||
const host = cookie.domain.startsWith('.') ? cookie.domain.slice(1) : cookie.domain
|
||||
|
||||
return prefix + host + (cookie.path || '')
|
||||
},
|
||||
}
|
||||
@@ -2,17 +2,21 @@
|
||||
"name": "@packages/extension",
|
||||
"version": "0.0.0-development",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"main": "lib-dist/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"check-ts": "tsc --noEmit && yarn -s tslint",
|
||||
"build-prod": "yarn build",
|
||||
"build:lib": "tsc -p tsconfig.lib.json",
|
||||
"build:v2": "webpack-cli",
|
||||
"build:v3": "tsc -p tsconfig.app.v3.json",
|
||||
"check-ts": "tsc -p tsconfig.json --noEmit && yarn -s tslint -p tsconfig.json",
|
||||
"clean": "gulp clean",
|
||||
"clean-deps": "rimraf node_modules",
|
||||
"postinstall": "echo '@packages/extension needs: yarn build'",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, .",
|
||||
"test": "yarn test-unit",
|
||||
"test-debug": "yarn test-unit --inspect-brk=5566",
|
||||
"test-unit": "cross-env NODE_ENV=test mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
|
||||
"test-debug": "vitest --inspect-brk --no-file-parallelism --test-timeout=0 --hook-timeout=0",
|
||||
"test-unit": "vitest run",
|
||||
"test-watch": "yarn test-unit --watch",
|
||||
"tslint": "tslint --config ../ts/tslint.json --project . --exclude ./dist/v2/background.js",
|
||||
"watch": "yarn build && chokidar 'app/**/*.*' 'app/*.*' -c 'yarn build'"
|
||||
@@ -24,25 +28,21 @@
|
||||
"devDependencies": {
|
||||
"@packages/icons": "0.0.0-development",
|
||||
"@packages/socket": "0.0.0-development",
|
||||
"chai": "3.5.0",
|
||||
"@types/webextension-polyfill": "0.12.4",
|
||||
"chokidar-cli": "2.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eol": "0.10.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"gulp": "4.0.2",
|
||||
"mocha": "3.5.3",
|
||||
"mock-require": "3.0.3",
|
||||
"rimraf": "6.0.1",
|
||||
"sinon": "7.3.2",
|
||||
"sinon-chai": "3.7.0",
|
||||
"ts-loader": "9.5.2",
|
||||
"vitest": "^3.2.4",
|
||||
"webextension-polyfill": "0.4.0",
|
||||
"webpack": "^5.88.2"
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"files": [
|
||||
"app",
|
||||
"dist",
|
||||
"lib",
|
||||
"app-dist",
|
||||
"lib-dist",
|
||||
"theme"
|
||||
],
|
||||
"nx": {
|
||||
|
||||
275
packages/extension/test/integration/v2/background.spec.ts
Normal file
275
packages/extension/test/integration/v2/background.spec.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import _ from 'lodash'
|
||||
import http from 'http'
|
||||
import { SocketIOServer } from '@packages/socket'
|
||||
import { connect } from '../../../app/v2/client'
|
||||
import EventEmitter from 'events'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import browser from 'webextension-polyfill'
|
||||
import { automation } from '../../../app/v2/background'
|
||||
|
||||
vi.mock('../../../app/v2/client', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
|
||||
return {
|
||||
// @ts-expect-error
|
||||
...actual,
|
||||
connect: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('webextension-polyfill', () => {
|
||||
return {
|
||||
default: {
|
||||
cookies: {
|
||||
onChanged: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
},
|
||||
downloads: {
|
||||
onCreated: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
onChanged: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
},
|
||||
runtime: {},
|
||||
browsingData: {
|
||||
remove: vi.fn(),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const PORT = 12345
|
||||
|
||||
describe('app/background', () => {
|
||||
let httpSrv: http.Server
|
||||
let server: http.Server
|
||||
let connectWrapper: (options?: Record<string, unknown>) => Promise<SocketShape>
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetAllMocks()
|
||||
vi.stubGlobal('window', {})
|
||||
|
||||
httpSrv = http.createServer()
|
||||
|
||||
// @ts-expect-error
|
||||
server = new SocketIOServer(httpSrv, { path: '/__socket' })
|
||||
|
||||
// use an event emitter and wrap in in a vitest mock to assert on calls
|
||||
const webSocketEventBus = new EventEmitter()
|
||||
const ws = {
|
||||
on: vi.fn().mockImplementation(webSocketEventBus.on),
|
||||
emit: vi.fn(webSocketEventBus.emit),
|
||||
} as unknown as SocketShape
|
||||
|
||||
vi.mocked(connect).mockReturnValue(ws)
|
||||
|
||||
browser.runtime.getBrowserInfo = vi.fn().mockResolvedValue({ name: 'Firefox' })
|
||||
|
||||
connectWrapper = async (options = {}) => {
|
||||
const ws = automation.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
// skip 'connect' and 'automation:client:connected' and trigger
|
||||
// the handler that kicks everything off
|
||||
ws.emit('automation:config', options)
|
||||
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
httpSrv.listen(PORT, resolve)
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
server.close()
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
httpSrv.close(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.connect', () => {
|
||||
it('emits \'automation:client:connected\'', async function () {
|
||||
const ws = automation.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
ws.emit('connect')
|
||||
|
||||
expect(ws.emit).toHaveBeenCalledWith('automation:client:connected')
|
||||
})
|
||||
|
||||
it('listens to cookie changes', async function () {
|
||||
await connectWrapper()
|
||||
|
||||
expect(browser.cookies.onChanged.addListener).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
describe('cookies', () => {
|
||||
it('onChanged does not emit when cause is overwrite', async function () {
|
||||
const ws = await connectWrapper()
|
||||
// @ts-expect-error
|
||||
const fn = browser.cookies.onChanged.addListener.mock.calls[0][0]
|
||||
|
||||
fn({ cause: 'overwrite' })
|
||||
|
||||
expect(ws.emit).not.toHaveBeenCalledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request change:cookie', async function () {
|
||||
const info = { cause: 'explicit', cookie: { name: 'foo', value: 'bar' } }
|
||||
|
||||
vi.mocked(browser.cookies.onChanged.addListener).mockImplementation((fn) => fn(info as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).toHaveBeenCalledWith('automation:push:request', 'change:cookie', info)
|
||||
})
|
||||
})
|
||||
|
||||
describe('downloads', () => {
|
||||
it('onCreated emits automation:push:request create:download', async function () {
|
||||
const downloadItem = {
|
||||
id: '1',
|
||||
filename: '/path/to/download.csv',
|
||||
mime: 'text/csv',
|
||||
url: 'http://localhost:1234/download.csv',
|
||||
}
|
||||
|
||||
vi.mocked(browser.downloads.onCreated.addListener).mockImplementation((fn) => fn(downloadItem as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).toHaveBeenCalledWith('automation:push:request', 'create:download', {
|
||||
id: `${downloadItem.id}`,
|
||||
filePath: downloadItem.filename,
|
||||
mime: downloadItem.mime,
|
||||
url: downloadItem.url,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request complete:download', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'complete',
|
||||
},
|
||||
}
|
||||
|
||||
vi.mocked(browser.downloads.onChanged.addListener).mockImplementation((fn) => fn(downloadDelta as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).toHaveBeenCalledWith('automation:push:request', 'complete:download', {
|
||||
id: `${downloadDelta.id}`,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request canceled:download', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'canceled',
|
||||
},
|
||||
}
|
||||
|
||||
vi.mocked(browser.downloads.onChanged.addListener).mockImplementation((fn) => fn(downloadDelta as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).toHaveBeenCalledWith('automation:push:request', 'canceled:download', {
|
||||
id: `${downloadDelta.id}`,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state does not exist', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
}
|
||||
|
||||
vi.mocked(browser.downloads.onChanged.addListener).mockImplementation((fn) => fn(downloadDelta as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).not.toHaveBeenCalledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state.current is not "complete"', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'inprogress',
|
||||
},
|
||||
}
|
||||
|
||||
vi.mocked(browser.downloads.onChanged.addListener).mockImplementation((fn) => fn(downloadDelta as any))
|
||||
|
||||
const ws = await connectWrapper()
|
||||
|
||||
expect(ws.emit).not.toHaveBeenCalledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('does not add downloads listener if in non-Firefox browser', async function () {
|
||||
vi.mocked(browser.runtime.getBrowserInfo).mockResolvedValue({ name: 'Chrome' } as any)
|
||||
|
||||
await connectWrapper()
|
||||
|
||||
expect(browser.downloads.onCreated.addListener).not.toHaveBeenCalled()
|
||||
expect(browser.downloads.onChanged.addListener).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('integration', () => {
|
||||
let socket: SocketShape
|
||||
|
||||
beforeEach(async function () {
|
||||
const { connect: connectActual } = await vi.importActual<typeof import('../../../app/v2/client')>('../../../app/v2/client')
|
||||
|
||||
vi.mocked(connect).mockImplementation(connectActual)
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
server.on('connection', (socket1) => {
|
||||
socket = socket1 as unknown as SocketShape
|
||||
|
||||
resolve()
|
||||
})
|
||||
|
||||
automation.connect(`http://localhost:${PORT}`, '/__socket')
|
||||
})
|
||||
})
|
||||
|
||||
describe('reset:browser:state', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(browser.browsingData.remove).mockImplementation((args: any, options: any) => {
|
||||
if (_.isEqual(args, {}) && _.isEqual(options, { cache: true, cookies: true, downloads: true, formData: true, history: true, indexedDB: true, localStorage: true, passwords: true, pluginData: true, serviceWorkers: true })) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Unexpected arguments'))
|
||||
})
|
||||
})
|
||||
|
||||
it('resets the browser state', function () {
|
||||
return new Promise<void>((resolve) => {
|
||||
socket.on('automation:response', (id: number, obj: { response: unknown }) => {
|
||||
expect(id).toEqual(123)
|
||||
expect(obj.response).toBeUndefined()
|
||||
|
||||
expect(browser.browsingData.remove).toHaveBeenCalled()
|
||||
|
||||
resolve()
|
||||
})
|
||||
|
||||
server.emit('automation:request', 123, 'reset:browser:state')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,244 +0,0 @@
|
||||
/* tslint:disable:no-empty */
|
||||
require('../../spec_helper')
|
||||
const _ = require('lodash')
|
||||
const http = require('http')
|
||||
const socket = require('@packages/socket')
|
||||
const mockRequire = require('mock-require')
|
||||
const client = require('../../../app/v2/client')
|
||||
|
||||
const browser = {
|
||||
cookies: {
|
||||
onChanged: {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
downloads: {
|
||||
onCreated: {
|
||||
addListener () {},
|
||||
},
|
||||
onChanged: {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
runtime: {},
|
||||
browsingData: {
|
||||
remove () {},
|
||||
},
|
||||
}
|
||||
|
||||
mockRequire('webextension-polyfill', browser)
|
||||
|
||||
const background = require('../../../app/v2/background')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const PORT = 12345
|
||||
|
||||
describe('app/background', () => {
|
||||
beforeEach(function (done) {
|
||||
global.window = {}
|
||||
|
||||
this.httpSrv = http.createServer()
|
||||
this.server = socket.server(this.httpSrv, { path: '/__socket' })
|
||||
|
||||
const ws = {
|
||||
on: sinon.stub(),
|
||||
emit: sinon.stub(),
|
||||
}
|
||||
|
||||
sinon.stub(client, 'connect').returns(ws)
|
||||
|
||||
browser.runtime.getBrowserInfo = sinon.stub().resolves({ name: 'Firefox' }),
|
||||
|
||||
this.connect = async (options = {}) => {
|
||||
const ws = background.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
// skip 'connect' and 'automation:client:connected' and trigger
|
||||
// the handler that kicks everything off
|
||||
await ws.on.withArgs('automation:config').args[0][1](options)
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
this.httpSrv.listen(PORT, done)
|
||||
})
|
||||
|
||||
afterEach(function (done) {
|
||||
this.server.close()
|
||||
|
||||
this.httpSrv.close(() => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
context('.connect', () => {
|
||||
it('emits \'automation:client:connected\'', async function () {
|
||||
const ws = background.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
await ws.on.withArgs('connect').args[0][1]()
|
||||
|
||||
expect(ws.emit).to.be.calledWith('automation:client:connected')
|
||||
})
|
||||
|
||||
it('listens to cookie changes', async function () {
|
||||
const addListener = sinon.stub(browser.cookies.onChanged, 'addListener')
|
||||
|
||||
await this.connect()
|
||||
|
||||
expect(addListener).to.be.calledOnce
|
||||
})
|
||||
})
|
||||
|
||||
context('cookies', () => {
|
||||
it('onChanged does not emit when cause is overwrite', async function () {
|
||||
const addListener = sinon.stub(browser.cookies.onChanged, 'addListener')
|
||||
const ws = await this.connect()
|
||||
const fn = addListener.getCall(0).args[0]
|
||||
|
||||
fn({ cause: 'overwrite' })
|
||||
|
||||
expect(ws.emit).not.to.be.calledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request change:cookie', async function () {
|
||||
const info = { cause: 'explicit', cookie: { name: 'foo', value: 'bar' } }
|
||||
|
||||
sinon.stub(browser.cookies.onChanged, 'addListener').yields(info)
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
expect(ws.emit).to.be.calledWith('automation:push:request', 'change:cookie', info)
|
||||
})
|
||||
})
|
||||
|
||||
context('downloads', () => {
|
||||
it('onCreated emits automation:push:request create:download', async function () {
|
||||
const downloadItem = {
|
||||
id: '1',
|
||||
filename: '/path/to/download.csv',
|
||||
mime: 'text/csv',
|
||||
url: 'http://localhost:1234/download.csv',
|
||||
}
|
||||
|
||||
sinon.stub(browser.downloads.onCreated, 'addListener').yields(downloadItem)
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
expect(ws.emit).to.be.calledWith('automation:push:request', 'create:download', {
|
||||
id: `${downloadItem.id}`,
|
||||
filePath: downloadItem.filename,
|
||||
mime: downloadItem.mime,
|
||||
url: downloadItem.url,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request complete:download', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'complete',
|
||||
},
|
||||
}
|
||||
|
||||
sinon.stub(browser.downloads.onChanged, 'addListener').yields(downloadDelta)
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
expect(ws.emit).to.be.calledWith('automation:push:request', 'complete:download', {
|
||||
id: `${downloadDelta.id}`,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request canceled:download', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'canceled',
|
||||
},
|
||||
}
|
||||
|
||||
sinon.stub(browser.downloads.onChanged, 'addListener').yields(downloadDelta)
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
expect(ws.emit).to.be.calledWith('automation:push:request', 'canceled:download', {
|
||||
id: `${downloadDelta.id}`,
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state does not exist', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
}
|
||||
const addListener = sinon.stub(browser.downloads.onChanged, 'addListener')
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
addListener.getCall(0).args[0](downloadDelta)
|
||||
|
||||
expect(ws.emit).not.to.be.calledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state.current is not "complete"', async function () {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'inprogress',
|
||||
},
|
||||
}
|
||||
const addListener = sinon.stub(browser.downloads.onChanged, 'addListener')
|
||||
|
||||
const ws = await this.connect()
|
||||
|
||||
addListener.getCall(0).args[0](downloadDelta)
|
||||
|
||||
expect(ws.emit).not.to.be.calledWith('automation:push:request')
|
||||
})
|
||||
|
||||
it('does not add downloads listener if in non-Firefox browser', async function () {
|
||||
browser.runtime.getBrowserInfo = undefined
|
||||
|
||||
const onCreated = sinon.stub(browser.downloads.onCreated, 'addListener')
|
||||
const onChanged = sinon.stub(browser.downloads.onChanged, 'addListener')
|
||||
|
||||
await this.connect()
|
||||
|
||||
expect(onCreated).not.to.be.called
|
||||
expect(onChanged).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
context('integration', () => {
|
||||
beforeEach(function (done) {
|
||||
done = _.once(done)
|
||||
|
||||
client.connect.restore()
|
||||
|
||||
this.server.on('connection', (socket1) => {
|
||||
this.socket = socket1
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
this.client = background.connect(`http://localhost:${PORT}`, '/__socket')
|
||||
})
|
||||
|
||||
describe('reset:browser:state', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(browser.browsingData, 'remove').withArgs({}, { cache: true, cookies: true, downloads: true, formData: true, history: true, indexedDB: true, localStorage: true, passwords: true, pluginData: true, serviceWorkers: true }).resolves()
|
||||
})
|
||||
|
||||
it('resets the browser state', function (done) {
|
||||
this.socket.on('automation:response', (id, obj) => {
|
||||
expect(id).to.eq(123)
|
||||
expect(obj.response).to.be.undefined
|
||||
|
||||
expect(browser.browsingData.remove).to.be.called
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
this.server.emit('automation:request', 123, 'reset:browser:state')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
124
packages/extension/test/integration/v3/content.spec.ts
Normal file
124
packages/extension/test/integration/v3/content.spec.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { describe, expect, beforeAll, beforeEach, it, vi } from 'vitest'
|
||||
|
||||
describe('app/v3/content', () => {
|
||||
let port: { onMessage: { addListener: () => void }, postMessage: () => void }
|
||||
let chrome: { runtime: { connect: () => { onMessage: { addListener: () => void } } } }
|
||||
let window: { addEventListener: () => void, postMessage: () => void }
|
||||
|
||||
beforeAll(async () => {
|
||||
port = {
|
||||
onMessage: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
postMessage: vi.fn(),
|
||||
}
|
||||
|
||||
chrome = {
|
||||
runtime: {
|
||||
connect: vi.fn().mockReturnValue(port),
|
||||
},
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
global.chrome = chrome
|
||||
|
||||
window = {
|
||||
addEventListener: vi.fn(),
|
||||
postMessage: vi.fn(),
|
||||
},
|
||||
|
||||
// @ts-expect-error
|
||||
global.window = window
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('adds window message listener and port onMessage listener', async () => {
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
expect(window.addEventListener).toHaveBeenCalledWith('message', expect.any(Function))
|
||||
expect(port.onMessage.addListener).toHaveBeenCalledWith(expect.any(Function))
|
||||
})
|
||||
|
||||
describe('messages from window (i.e Cypress)', () => {
|
||||
describe('on cypress:extension:activate:main:tab', () => {
|
||||
const data = { message: 'cypress:extension:activate:main:tab' }
|
||||
|
||||
it('posts message to port', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(window.addEventListener).mockImplementation((event: MessageEvent, callback: (event: MessageEvent) => void) => callback({ data, source: window } as any))
|
||||
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(port.postMessage).toHaveBeenCalledWith({
|
||||
message: 'activate:main:tab',
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if source is not the same window', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(window.addEventListener).mockImplementation((event: MessageEvent, callback: (event: MessageEvent) => void) => callback({ data, source: {} } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(port.postMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('on cypress:extension:url:changed', () => {
|
||||
const data = { message: 'cypress:extension:url:changed', url: 'the://url' }
|
||||
|
||||
it('posts message to port', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(window.addEventListener).mockImplementation((event: MessageEvent, callback: (event: MessageEvent) => void) => callback({ data, source: window } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(port.postMessage).toHaveBeenCalledWith({
|
||||
message: 'url:changed',
|
||||
url: data.url,
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if source is not the same window', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(window.addEventListener).mockImplementation((event: MessageEvent, callback: (event: MessageEvent) => void) => callback({ data, source: {} } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(port.postMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not supported', async () => {
|
||||
const data = { message: 'unsupported' }
|
||||
|
||||
// @ts-expect-error
|
||||
vi.mocked(window.addEventListener).mockImplementation((event: MessageEvent, callback: (event: MessageEvent) => void) => callback({ data, source: window } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(port.postMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('messages from port (i.e. service worker)', () => {
|
||||
describe('on main:tab:activated', () => {
|
||||
it('posts message to window', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'main:tab:activated' } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(window.postMessage).toHaveBeenCalledWith({ message: 'cypress:extension:main:tab:activated' }, '*')
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not main:tab:activated', async () => {
|
||||
const data = { message: 'unsupported' }
|
||||
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ data, source: window } as any))
|
||||
await vi.importActual('../../../app/v3/content')
|
||||
|
||||
expect(window.postMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,108 +0,0 @@
|
||||
require('../../spec_helper')
|
||||
|
||||
describe('app/v3/content', () => {
|
||||
let port
|
||||
let chrome
|
||||
let window
|
||||
|
||||
before(() => {
|
||||
port = {
|
||||
onMessage: {
|
||||
addListener: sinon.stub(),
|
||||
},
|
||||
postMessage: sinon.stub(),
|
||||
}
|
||||
|
||||
chrome = {
|
||||
runtime: {
|
||||
connect: sinon.stub().returns(port),
|
||||
},
|
||||
}
|
||||
|
||||
global.chrome = chrome
|
||||
|
||||
window = {
|
||||
addEventListener: sinon.stub(),
|
||||
postMessage: sinon.stub(),
|
||||
},
|
||||
|
||||
global.window = window
|
||||
|
||||
require('../../../app/v3/content')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
port.postMessage.reset()
|
||||
window.postMessage.reset()
|
||||
})
|
||||
|
||||
it('adds window message listener and port onMessage listener', () => {
|
||||
expect(window.addEventListener).to.be.calledWith('message', sinon.match.func)
|
||||
expect(port.onMessage.addListener).to.be.calledWith(sinon.match.func)
|
||||
})
|
||||
|
||||
describe('messages from window (i.e Cypress)', () => {
|
||||
describe('on cypress:extension:activate:main:tab', () => {
|
||||
const data = { message: 'cypress:extension:activate:main:tab' }
|
||||
|
||||
it('posts message to port', () => {
|
||||
window.addEventListener.yield({ data, source: window })
|
||||
|
||||
expect(port.postMessage).to.be.calledWith({
|
||||
message: 'activate:main:tab',
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if source is not the same window', () => {
|
||||
window.addEventListener.yield({ data, source: {} })
|
||||
|
||||
expect(port.postMessage).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('on cypress:extension:url:changed', () => {
|
||||
const data = { message: 'cypress:extension:url:changed', url: 'the://url' }
|
||||
|
||||
it('posts message to port', () => {
|
||||
window.addEventListener.yield({ data, source: window })
|
||||
|
||||
expect(port.postMessage).to.be.calledWith({
|
||||
message: 'url:changed',
|
||||
url: data.url,
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if source is not the same window', () => {
|
||||
window.addEventListener.yield({ data, source: {} })
|
||||
|
||||
expect(port.postMessage).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not supported', () => {
|
||||
const data = { message: 'unsupported' }
|
||||
|
||||
window.addEventListener.yield({ data, source: window })
|
||||
|
||||
expect(port.postMessage).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('messages from port (i.e. service worker)', () => {
|
||||
describe('on main:tab:activated', () => {
|
||||
it('posts message to window', () => {
|
||||
port.onMessage.addListener.yield({ message: 'main:tab:activated' })
|
||||
|
||||
expect(window.postMessage).to.be.calledWith({ message: 'cypress:extension:main:tab:activated' }, '*')
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not main:tab:activated', () => {
|
||||
const data = { message: 'unsupported' }
|
||||
|
||||
port.onMessage.addListener.yield({ data, source: window })
|
||||
|
||||
expect(window.postMessage).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
157
packages/extension/test/integration/v3/service-worker.spec.ts
Normal file
157
packages/extension/test/integration/v3/service-worker.spec.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest'
|
||||
|
||||
describe('app/v3/service-worker', () => {
|
||||
let chrome: { runtime: { onConnect: { addListener: () => void } }, tabs: { query: () => void, update: () => void }, storage: { local: { set: () => void, get: () => void } } }
|
||||
let port: { onMessage: { addListener: () => void }, postMessage: () => void }
|
||||
|
||||
beforeAll(() => {
|
||||
chrome = {
|
||||
runtime: {
|
||||
onConnect: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
query: vi.fn(),
|
||||
update: vi.fn(),
|
||||
},
|
||||
storage: {
|
||||
local: {
|
||||
set: vi.fn(),
|
||||
get: vi.fn(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
global.chrome = chrome
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
vi.clearAllMocks()
|
||||
|
||||
port = {
|
||||
onMessage: {
|
||||
addListener: vi.fn(),
|
||||
},
|
||||
postMessage: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
it('adds onConnect listener', async () => {
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
expect(chrome.runtime.onConnect.addListener).toHaveBeenCalledWith(expect.any(Function))
|
||||
})
|
||||
|
||||
it('adds port onMessage listener', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.runtime.onConnect.addListener).mockImplementation((fn: (port: { onMessage: { addListener: () => void } }) => void) => fn(port))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(port.onMessage.addListener).toHaveBeenCalledWith(expect.any(Function))
|
||||
})
|
||||
|
||||
describe('on message', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.runtime.onConnect.addListener).mockImplementation((fn: (port: { onMessage: { addListener: () => void } }) => void) => fn(port))
|
||||
})
|
||||
|
||||
describe('activate:main:tab', () => {
|
||||
const tab1 = { id: '1', url: 'the://url' }
|
||||
const tab2 = { id: '2', url: 'some://other.url' }
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.tabs.query).mockResolvedValue([tab1, tab2])
|
||||
})
|
||||
|
||||
describe('when there is a most recent url', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.storage.local.get).mockImplementation((key: string, callback: (result: { mostRecentUrl: string }) => void) => callback({ mostRecentUrl: tab1.url }))
|
||||
})
|
||||
|
||||
it('activates the tab matching the url', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'activate:main:tab' } as any))
|
||||
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(chrome.tabs.update).toHaveBeenCalledWith(tab1.id, { active: true })
|
||||
})
|
||||
|
||||
describe('but no tab matches the most recent url', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.tabs.query).mockResolvedValue([tab2])
|
||||
})
|
||||
|
||||
it('does not try to activate any tabs', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'activate:main:tab' } as any))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(chrome.tabs.update).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('and chrome throws an error while activating the tab', () => {
|
||||
let err: Error
|
||||
|
||||
beforeEach(() => {
|
||||
vi.spyOn(console, 'log').mockImplementation(() => undefined)
|
||||
err = new Error('uh oh')
|
||||
vi.mocked(chrome.tabs.update).mockRejectedValue(err)
|
||||
})
|
||||
|
||||
it('is a noop, logging the error', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'activate:main:tab' } as any))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).toHaveBeenCalledWith('Activating main Cypress tab errored:', err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is not a most recent url', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(chrome.storage.local.get).mockImplementation((key: string, callback: (result: { mostRecentUrl: string }) => void) => callback({}))
|
||||
})
|
||||
|
||||
it('does not try to activate any tabs', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'activate:main:tab' } as any))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(chrome.tabs.update).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('url:changed', () => {
|
||||
it('sets the mostRecentUrl', async () => {
|
||||
const url = 'some://url'
|
||||
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'url:changed', url } as any))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(chrome.storage.local.set).toHaveBeenCalledWith({ mostRecentUrl: url })
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not a supported message', async () => {
|
||||
// @ts-expect-error
|
||||
vi.mocked(port.onMessage.addListener).mockImplementation((callback: (event: MessageEvent) => void) => callback({ message: 'unsupported' } as any))
|
||||
await vi.importActual('../../../app/v3/service-worker')
|
||||
|
||||
expect(chrome.tabs.update).not.toHaveBeenCalled()
|
||||
expect(chrome.storage.local.set).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,137 +0,0 @@
|
||||
require('../../spec_helper')
|
||||
|
||||
describe('app/v3/service-worker', () => {
|
||||
let chrome
|
||||
let port
|
||||
|
||||
before(() => {
|
||||
chrome = {
|
||||
runtime: {
|
||||
onConnect: {
|
||||
addListener: sinon.stub(),
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
query: sinon.stub(),
|
||||
update: sinon.stub(),
|
||||
},
|
||||
storage: {
|
||||
local: {
|
||||
set: sinon.stub(),
|
||||
get: sinon.stub(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
global.chrome = chrome
|
||||
|
||||
require('../../../app/v3/service-worker')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
chrome.tabs.query.reset()
|
||||
chrome.tabs.update.reset()
|
||||
chrome.storage.local.set.reset()
|
||||
chrome.storage.local.get.reset()
|
||||
|
||||
port = {
|
||||
onMessage: {
|
||||
addListener: sinon.stub(),
|
||||
},
|
||||
postMessage: sinon.stub(),
|
||||
}
|
||||
})
|
||||
|
||||
it('adds onConnect listener', () => {
|
||||
expect(chrome.runtime.onConnect.addListener).to.be.calledWith(sinon.match.func)
|
||||
})
|
||||
|
||||
it('adds port onMessage listener', () => {
|
||||
chrome.runtime.onConnect.addListener.yield(port)
|
||||
|
||||
expect(port.onMessage.addListener).to.be.calledWith(sinon.match.func)
|
||||
})
|
||||
|
||||
describe('on message', () => {
|
||||
beforeEach(() => {
|
||||
chrome.runtime.onConnect.addListener.yield(port)
|
||||
})
|
||||
|
||||
describe('activate:main:tab', () => {
|
||||
const tab1 = { id: '1', url: 'the://url' }
|
||||
const tab2 = { id: '2', url: 'some://other.url' }
|
||||
|
||||
beforeEach(() => {
|
||||
chrome.tabs.query.resolves([tab1, tab2])
|
||||
})
|
||||
|
||||
describe('when there is a most recent url', () => {
|
||||
beforeEach(() => {
|
||||
chrome.storage.local.get.callsArgWith(1, { mostRecentUrl: tab1.url })
|
||||
})
|
||||
|
||||
it('activates the tab matching the url', async () => {
|
||||
await port.onMessage.addListener.yield({ message: 'activate:main:tab' })[0]
|
||||
|
||||
expect(chrome.tabs.update).to.be.calledWith(tab1.id, { active: true })
|
||||
})
|
||||
|
||||
describe('but no tab matches the most recent url', () => {
|
||||
beforeEach(() => {
|
||||
chrome.tabs.query.reset()
|
||||
chrome.tabs.query.resolves([tab2])
|
||||
})
|
||||
|
||||
it('does not try to activate any tabs', async () => {
|
||||
await port.onMessage.addListener.yield({ message: 'activate:main:tab' })[0]
|
||||
expect(chrome.tabs.update).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('and chrome throws an error while activating the tab', () => {
|
||||
let err
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(console, 'log')
|
||||
err = new Error('uh oh')
|
||||
chrome.tabs.update.rejects(err)
|
||||
})
|
||||
|
||||
it('is a noop, logging the error', async () => {
|
||||
await port.onMessage.addListener.yield({ message: 'activate:main:tab' })[0]
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).to.be.calledWith('Activating main Cypress tab errored:', err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is not a most recent url', () => {
|
||||
beforeEach(() => {
|
||||
chrome.storage.local.get.callsArgWith(1, {})
|
||||
})
|
||||
|
||||
it('does not try to activate any tabs', async () => {
|
||||
await port.onMessage.addListener.yield({ message: 'activate:main:tab' })[0]
|
||||
expect(chrome.tabs.update).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('url:changed', () => {
|
||||
it('sets the mostRecentUrl', async () => {
|
||||
const url = 'some://url'
|
||||
|
||||
await port.onMessage.addListener.yield({ message: 'url:changed', url })[0]
|
||||
expect(chrome.storage.local.set).to.be.calledWith({ mostRecentUrl: url })
|
||||
})
|
||||
})
|
||||
|
||||
it('is a noop if message is not a supported message', async () => {
|
||||
await port.onMessage.addListener.yield({ message: 'unsupported' })[0]
|
||||
|
||||
expect(chrome.tabs.update).not.to.be.called
|
||||
expect(chrome.storage.local.set).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
test/unit
|
||||
test/integration
|
||||
--reporter spec
|
||||
--recursive
|
||||
@@ -1,12 +0,0 @@
|
||||
const chai = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const sinonChai = require('sinon-chai')
|
||||
|
||||
chai.use(sinonChai)
|
||||
|
||||
global.sinon = sinon
|
||||
global.expect = chai.expect
|
||||
|
||||
afterEach(() => {
|
||||
return sinon.restore()
|
||||
})
|
||||
111
packages/extension/test/unit/extension.spec.ts
Normal file
111
packages/extension/test/unit/extension.spec.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { exec } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import * as extension from '../../lib/index'
|
||||
|
||||
vi.mock('../../lib/index', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
|
||||
return {
|
||||
// @ts-expect-error
|
||||
...actual,
|
||||
getPathToExtension: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
const cwd = process.cwd()
|
||||
|
||||
describe('Extension', () => {
|
||||
describe('.getPathToExtension', () => {
|
||||
beforeEach(async () => {
|
||||
const { getPathToExtension } = await vi.importActual<typeof import('../../lib/index')>('../../lib/index')
|
||||
|
||||
// use the actual implementation for these tests
|
||||
vi.mocked(extension.getPathToExtension).mockImplementation(getPathToExtension)
|
||||
})
|
||||
|
||||
it('returns path to app-dist/v2', () => {
|
||||
const result = extension.getPathToExtension()
|
||||
const expected = path.join(cwd, 'app-dist', 'v2')
|
||||
|
||||
expect(path.normalize(result)).toEqual(path.normalize(expected))
|
||||
})
|
||||
|
||||
it('returns path to files in app-dist/v2', () => {
|
||||
const result = extension.getPathToExtension('background.js')
|
||||
const expected = path.join(cwd, '/app-dist/v2/background.js')
|
||||
|
||||
expect(path.normalize(result)).toEqual(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getPathToV3Extension', () => {
|
||||
it('returns path to app-dist/v3', () => {
|
||||
const result = extension.getPathToV3Extension()
|
||||
const expected = path.join(cwd, 'app-dist', 'v3')
|
||||
|
||||
expect(path.normalize(result)).toEqual(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getPathToTheme', () => {
|
||||
it('returns path to theme', () => {
|
||||
const result = extension.getPathToTheme()
|
||||
const expected = path.join(cwd, 'theme')
|
||||
|
||||
expect(path.normalize(result)).toEqual(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getPathToRoot', () => {
|
||||
it('returns path to root', () => {
|
||||
expect(extension.getPathToRoot()).toEqual(cwd)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.setHostAndPath', () => {
|
||||
let src: string
|
||||
|
||||
beforeEach(function () {
|
||||
src = path.join(cwd, 'test', 'helpers', 'background.js')
|
||||
|
||||
vi.mocked(extension.getPathToExtension).mockImplementation((file) => {
|
||||
if (file === 'background.js') {
|
||||
return src
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected file: ${file}`)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not mutate background.js', async () => {
|
||||
const str = await fs.readFile(src, 'utf8')
|
||||
|
||||
await extension.setHostAndPath('http://dev.local:8080', '/__foo')
|
||||
|
||||
const str2 = await fs.readFile(src, 'utf8')
|
||||
|
||||
expect(str).toEqual(str2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('manifest', () => {
|
||||
it('has a key that resolves to the static extension ID', async () => {
|
||||
const manifest = await fs.readJson(path.join(cwd, 'app/v2/manifest.json'))
|
||||
const cmd = `echo \"${manifest.key}\" | openssl base64 -d -A | shasum -a 256 | head -c32 | tr 0-9a-f a-p`
|
||||
|
||||
const stdout = await new Promise((resolve, reject) => {
|
||||
exec(cmd, (error, stdout) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
}
|
||||
|
||||
resolve(stdout)
|
||||
})
|
||||
})
|
||||
|
||||
expect(stdout).toEqual('caljajdfkjjjdehjdoimjkkakekklcck')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,136 +0,0 @@
|
||||
require('../spec_helper')
|
||||
|
||||
let { exec } = require('child_process')
|
||||
let fs = require('fs-extra')
|
||||
const eol = require('eol')
|
||||
const path = require('path')
|
||||
const Promise = require('bluebird')
|
||||
const extension = require('../../index')
|
||||
|
||||
const cwd = process.cwd()
|
||||
|
||||
fs = Promise.promisifyAll(fs)
|
||||
exec = Promise.promisify(exec)
|
||||
|
||||
describe('Extension', () => {
|
||||
context('.getCookieUrl', () => {
|
||||
it('returns cookie url', () => {
|
||||
expect(extension.getCookieUrl({
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
path: '/foo/bar',
|
||||
domain: 'www.google.com',
|
||||
secure: true,
|
||||
})).to.eq('https://www.google.com/foo/bar')
|
||||
})
|
||||
})
|
||||
|
||||
context('.getPathToExtension', () => {
|
||||
it('returns path to dist/v2', () => {
|
||||
const result = extension.getPathToExtension()
|
||||
const expected = path.join(cwd, 'dist', 'v2')
|
||||
|
||||
expect(path.normalize(result)).to.eq(path.normalize(expected))
|
||||
})
|
||||
|
||||
it('returns path to files in dist/v2', () => {
|
||||
const result = extension.getPathToExtension('background.js')
|
||||
const expected = path.join(cwd, '/dist/v2/background.js')
|
||||
|
||||
expect(path.normalize(result)).to.eq(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
context('.getPathToV3Extension', () => {
|
||||
it('returns path to dist/v3', () => {
|
||||
const result = extension.getPathToV3Extension()
|
||||
const expected = path.join(cwd, 'dist', 'v3')
|
||||
|
||||
expect(path.normalize(result)).to.eq(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
context('.getPathToTheme', () => {
|
||||
it('returns path to theme', () => {
|
||||
const result = extension.getPathToTheme()
|
||||
const expected = path.join(cwd, 'theme')
|
||||
|
||||
expect(path.normalize(result)).to.eq(path.normalize(expected))
|
||||
})
|
||||
})
|
||||
|
||||
context('.getPathToRoot', () => {
|
||||
it('returns path to root', () => {
|
||||
expect(extension.getPathToRoot()).to.eq(cwd)
|
||||
})
|
||||
})
|
||||
|
||||
context('.setHostAndPath', () => {
|
||||
beforeEach(function () {
|
||||
this.src = path.join(cwd, 'test', 'helpers', 'background.js')
|
||||
|
||||
return sinon.stub(extension, 'getPathToExtension')
|
||||
.withArgs('background.js').returns(this.src)
|
||||
})
|
||||
|
||||
it('rewrites the background.js source', () => {
|
||||
return extension.setHostAndPath('http://dev.local:8080', '/__foo')
|
||||
.then((str) => {
|
||||
const result = eol.auto(str)
|
||||
const expected = eol.auto(`\
|
||||
(function() {
|
||||
var HOST, PATH, automation, client, fail, invoke,
|
||||
slice = [].slice;
|
||||
|
||||
HOST = "http://dev.local:8080";
|
||||
|
||||
PATH = "/__foo";
|
||||
|
||||
client = io.connect(HOST, {
|
||||
path: PATH
|
||||
});
|
||||
|
||||
automation = {
|
||||
getAllCookies: function(filter, fn) {
|
||||
if (filter == null) {
|
||||
filter = {};
|
||||
}
|
||||
return chrome.cookies.getAll(filter, fn);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
\
|
||||
`)
|
||||
|
||||
expect(result).to.eq(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not mutate background.js', function () {
|
||||
return fs.readFileAsync(this.src, 'utf8')
|
||||
.then((str) => {
|
||||
return extension.setHostAndPath('http://dev.local:8080', '/__foo')
|
||||
.then(() => {
|
||||
return fs.readFileAsync(this.src, 'utf8')
|
||||
}).then((str2) => {
|
||||
expect(str).to.eq(str2)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('manifest', () => {
|
||||
it('has a key that resolves to the static extension ID', () => {
|
||||
return fs.readJsonAsync(path.join(cwd, 'app/v2/manifest.json'))
|
||||
.then((manifest) => {
|
||||
const cmd = `echo \"${manifest.key}\" | openssl base64 -d -A | shasum -a 256 | head -c32 | tr 0-9a-f a-p`
|
||||
|
||||
return exec(cmd)
|
||||
.then((stdout) => {
|
||||
expect(stdout).to.eq('caljajdfkjjjdehjdoimjkkakekklcck')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
12
packages/extension/tsconfig.app.v2.json
Normal file
12
packages/extension/tsconfig.app.v2.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"app/v2/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./app/v2",
|
||||
"outDir": "./app-dist/v2",
|
||||
"target": "ES2022",
|
||||
"module": "Es2022"
|
||||
}
|
||||
}
|
||||
12
packages/extension/tsconfig.app.v3.json
Normal file
12
packages/extension/tsconfig.app.v3.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"app/v3**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./app/v3",
|
||||
"outDir": "./app-dist/v3",
|
||||
"target": "ES2022",
|
||||
"module": "ES2022"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
{
|
||||
"extends": "../ts/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"allowJs": true,
|
||||
"strict": false
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"esModuleInterop": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
13
packages/extension/tsconfig.lib.json
Normal file
13
packages/extension/tsconfig.lib.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"lib/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"rootDir": "./lib",
|
||||
"outDir": "./lib-dist",
|
||||
"target": "ES2022",
|
||||
"module": "CommonJS"
|
||||
}
|
||||
}
|
||||
9
packages/extension/vitest.config.ts
Normal file
9
packages/extension/vitest.config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['test/**/*.spec.ts'],
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
},
|
||||
})
|
||||
@@ -1,9 +1,9 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
mode: process.env.NODE_ENV || 'development',
|
||||
entry: './app/v2/init.js',
|
||||
entry: './app/v2/init.ts',
|
||||
// https://github.com/cypress-io/cypress/issues/15032
|
||||
// Default webpack output setting is "eval".
|
||||
// Chrome doesn't allow "eval" inside extensions.
|
||||
@@ -12,7 +12,14 @@ module.exports = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
use: [
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve(import.meta.dirname, 'tsconfig.app.v2.json'),
|
||||
},
|
||||
},
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
@@ -22,7 +29,7 @@ module.exports = {
|
||||
},
|
||||
output: {
|
||||
filename: 'background.js',
|
||||
path: path.resolve(__dirname, 'dist', 'v2'),
|
||||
path: path.resolve(import.meta.dirname, 'app-dist', 'v2'),
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
} from '@urql/core'
|
||||
import { devtoolsExchange } from '@urql/devtools'
|
||||
import { useToast } from 'vue-toastification'
|
||||
import type { SocketShape } from '@packages/socket/lib/types'
|
||||
import { client } from '@packages/socket/lib/browser'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import { client } from '@packages/socket/browser/client'
|
||||
import { createClient as createWsClient } from 'graphql-ws'
|
||||
|
||||
import { cacheExchange as graphcacheExchange } from '@urql/exchange-graphcache'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pipe, tap } from 'wonka'
|
||||
import type { Exchange, Operation, OperationResult } from '@urql/core'
|
||||
import type { SocketShape } from '@packages/socket/lib/types'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import type { DefinitionNode, DocumentNode, OperationDefinitionNode } from 'graphql'
|
||||
|
||||
export const pubSubExchange = (io: SocketShape): Exchange => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash'
|
||||
import type { SocketShape } from '@packages/socket/lib/types'
|
||||
import type { SocketShape } from '@packages/socket/browser/client'
|
||||
import type { ClientOptions } from '@urql/core'
|
||||
|
||||
export const urqlFetchSocketAdapter = (io: SocketShape): ClientOptions['fetch'] => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import url from 'url'
|
||||
import DebuggingProxy from '@cypress/debugging-proxy'
|
||||
import Request from '@cypress/request-promise'
|
||||
|
||||
import * as socketIo from '@packages/socket/lib/browser'
|
||||
import * as socketIo from '@packages/socket/browser/client'
|
||||
import {
|
||||
buildConnectReqHead,
|
||||
createProxySock,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as errors from '@packages/server/lib/errors'
|
||||
import errors from '@packages/errors'
|
||||
|
||||
import type { HttpMiddleware } from '.'
|
||||
import type { Readable } from 'stream'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import _ from 'lodash'
|
||||
import Debug from 'debug'
|
||||
import extension from '@packages/extension'
|
||||
import { isHostOnlyCookie } from '../browsers/cdp_automation'
|
||||
import type { SerializableAutomationCookie } from '../util/cookies'
|
||||
|
||||
@@ -32,6 +31,19 @@ const normalizeCookies = (cookies: (SerializableAutomationCookie | AutomationCoo
|
||||
return _.map(cookies, normalizeCookieProps) as AutomationCookie[]
|
||||
}
|
||||
|
||||
const getCookieUrl = (cookie: {
|
||||
secure?: boolean | null
|
||||
domain?: string | null
|
||||
path?: string | null
|
||||
} = {}) => {
|
||||
const prefix = cookie.secure ? 'https://' : 'http://'
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/6375
|
||||
const host = cookie.domain?.startsWith('.') ? cookie.domain.slice(1) : cookie.domain
|
||||
|
||||
return prefix + host + (cookie.path || '')
|
||||
}
|
||||
|
||||
const normalizeCookieProps = function (automationCookie: SerializableAutomationCookie | AutomationCookie | null) {
|
||||
if (!automationCookie) {
|
||||
return automationCookie
|
||||
@@ -151,7 +163,7 @@ export class Cookies {
|
||||
|
||||
// lets construct the url ourselves right now
|
||||
// unless we already have a URL
|
||||
cookie.url = data.url != null ? data.url : extension.getCookieUrl(data)
|
||||
cookie.url = data.url != null ? data.url : getCookieUrl(data)
|
||||
|
||||
debug('set:cookie %o', cookie)
|
||||
|
||||
@@ -175,7 +187,7 @@ export class Cookies {
|
||||
|
||||
// lets construct the url ourselves right now
|
||||
// unless we already have a URL
|
||||
cookie.url = data.url != null ? data.url : extension.getCookieUrl(data)
|
||||
cookie.url = data.url != null ? data.url : getCookieUrl(data)
|
||||
|
||||
return cookie
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import la from 'lazy-ass'
|
||||
import _ from 'lodash'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import extension from '@packages/extension'
|
||||
import * as extension from '@packages/extension'
|
||||
import mime from 'mime'
|
||||
import { launch } from '@packages/launcher'
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { Automation } from '../automation'
|
||||
import memory from './memory'
|
||||
|
||||
import type { BrowserLaunchOpts, BrowserNewTabOpts, ProtocolManagerShape, CyPromptManagerShape, RunModeVideoApi } from '@packages/types'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
import type { CDPSocketServer } from '@packages/socket'
|
||||
import { DEFAULT_CHROME_FLAGS } from '../util/chromium_flags'
|
||||
|
||||
const debug = debugModule('cypress:server:browsers:chrome')
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { Browser, BrowserInstance, GracefulShutdownOptions } from './types'
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import type { Automation } from '../automation'
|
||||
import type { BrowserLaunchOpts, Preferences, ProtocolManagerShape, CyPromptManagerShape, RunModeVideoApi } from '@packages/types'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
import type { CDPSocketServer } from '@packages/socket'
|
||||
import memory from './memory'
|
||||
import { BrowserCriClient } from './browser-cri-client'
|
||||
import { getRemoteDebuggingPort } from '../util/electron-app'
|
||||
|
||||
@@ -10,7 +10,7 @@ import { BROWSER_FAMILY, BrowserLaunchOpts, BrowserNewTabOpts, FoundBrowser, Pro
|
||||
import type { Browser, BrowserInstance, BrowserLauncher } from './types'
|
||||
import type { Automation } from '../automation'
|
||||
import type { DataContext } from '@packages/data-context'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
import type { CDPSocketServer } from '@packages/socket'
|
||||
|
||||
const debug = Debug('cypress:server:browsers')
|
||||
const isBrowserFamily = (browser: string) => BROWSER_FAMILY.includes(browser)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { FoundBrowser, BrowserLaunchOpts, BrowserNewTabOpts, ProtocolManagerShape, CyPromptManagerShape } from '@packages/types'
|
||||
import type { EventEmitter } from 'events'
|
||||
import type { Automation } from '../automation'
|
||||
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
import type { CDPSocketServer } from '@packages/socket'
|
||||
|
||||
export type Browser = FoundBrowser & {
|
||||
majorVersion: number | string
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getCtx } from '@packages/data-context'
|
||||
import { handleGraphQLSocketRequest } from '@packages/data-context/graphql/makeGraphQLServer'
|
||||
import { onNetStubbingEvent } from '@packages/net-stubbing'
|
||||
import * as socketIo from '@packages/socket'
|
||||
import { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
|
||||
import { CDPSocketServer } from '@packages/socket'
|
||||
|
||||
import * as errors from './errors'
|
||||
import fixture from './fixture'
|
||||
|
||||
@@ -5,7 +5,9 @@ const ws = require('ws')
|
||||
const httpsProxyAgent = require('https-proxy-agent')
|
||||
const evilDns = require('evil-dns')
|
||||
const Promise = require('bluebird')
|
||||
const socketIo = require(`@packages/socket/lib/browser`)
|
||||
// NOTE: we need to import the client from the lib directory because the browser/client directory is compiled to ESM.
|
||||
// we are unable to import ESM into a CommonJS test context, even if we await import() the module.
|
||||
const socketIo = require('@packages/socket/lib/client')
|
||||
const httpsServer = require(`@packages/https-proxy/test/helpers/https_server`)
|
||||
const config = require(`../../lib/config`)
|
||||
const { ServerBase } = require(`../../lib/server-base`)
|
||||
|
||||
@@ -337,7 +337,7 @@ describe('lib/browsers/firefox', () => {
|
||||
|
||||
it('writes extension and ensure write access', async function () {
|
||||
mockfs({
|
||||
[path.resolve(`${__dirname }../../../../../extension/dist/v2`)]: {
|
||||
[path.resolve(`${__dirname }../../../../../extension/app-dist/v2`)]: {
|
||||
'background.js': mockfs.file({
|
||||
mode: 0o444,
|
||||
}),
|
||||
|
||||
@@ -3,7 +3,10 @@ require('../spec_helper')
|
||||
const _ = require('lodash')
|
||||
const path = require('path')
|
||||
const httpsAgent = require('https-proxy-agent')
|
||||
const socketIo = require('@packages/socket/lib/browser')
|
||||
// NOTE: we need to import the client from the lib directory because the browser/client directory is compiled to ESM.
|
||||
// we are unable to import ESM into a CommonJS test context, even if we await import() the module.
|
||||
const socketIo = require('@packages/socket/lib/client')
|
||||
|
||||
const Fixtures = require('@tooling/system-tests')
|
||||
|
||||
const errors = require('../../lib/errors')
|
||||
@@ -28,6 +31,19 @@ describe('lib/socket', () => {
|
||||
path: 'path-to-browser-one',
|
||||
}
|
||||
|
||||
// needed to run these tests locally
|
||||
// sinon.stub(ctx.browser, 'machineBrowsers').resolves([
|
||||
// {
|
||||
// channel: 'stable',
|
||||
// displayName: 'Electron',
|
||||
// family: 'chromium',
|
||||
// majorVersion: '123',
|
||||
// name: 'electron',
|
||||
// path: 'path-to-browser-one',
|
||||
// version: '123.45.67',
|
||||
// },
|
||||
// ])
|
||||
|
||||
// Don't bother initializing the child process, etc for this
|
||||
sinon.stub(ctx.actions.project, 'initializeActiveProject')
|
||||
sinon.stub(preprocessor.emitter, 'on')
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
**/tsconfig.json
|
||||
|
||||
cjs/
|
||||
esm/
|
||||
browser/
|
||||
3
packages/socket/.gitignore
vendored
Normal file
3
packages/socket/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
cjs/
|
||||
esm/
|
||||
browser/
|
||||
@@ -1,32 +1,19 @@
|
||||
# Socket
|
||||
|
||||
This is a shared lib for holding both the `socket.io` server and client.
|
||||
This is a shared lib for holding both the `socket.io` Cypress implements of the server and client.
|
||||
|
||||
## Using
|
||||
|
||||
```javascript
|
||||
const socket = require("@packages/socket")
|
||||
|
||||
// returns
|
||||
{
|
||||
server: require("socket.io"),
|
||||
getPathToClientSource: function () {
|
||||
// returns path to the client 'socket.io.js' file
|
||||
// for use in the browser
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
const socket = require("@packages/socket")
|
||||
|
||||
// server usage
|
||||
const srv = require("http").createServer()
|
||||
const io = socket.server(srv)
|
||||
const io = new SocketIOServer(srv)
|
||||
io.on("connection", function(){})
|
||||
|
||||
// client usage
|
||||
const { client } = require("@packages/socket/lib/client")
|
||||
const { client } = require("@packages/socket/browser/client")
|
||||
const client = socket.client("http://localhost:2020")
|
||||
client.on("connect", function(){})
|
||||
client.on("event", function(){})
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
if (process.env.CYPRESS_INTERNAL_ENV !== 'production') {
|
||||
require('@packages/ts/register')
|
||||
}
|
||||
|
||||
module.exports = require('./lib/socket')
|
||||
@@ -1,8 +1,6 @@
|
||||
import io, { ManagerOptions, SocketOptions } from 'socket.io-client'
|
||||
import { CDPBrowserSocket } from './cdp-browser'
|
||||
import type { SocketShape } from './types'
|
||||
|
||||
export type { Socket } from 'socket.io-client'
|
||||
import type { SocketShape } from './cdp-browser'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -26,8 +24,7 @@ export function client (uri: string, opts?: Partial<ManagerOptions & SocketOptio
|
||||
// Connect the socket regardless of whether or not we have newly created it
|
||||
window.cypressSockets[fullNamespace].connect()
|
||||
|
||||
// @ts-expect-error TODO: fix type
|
||||
return window.cypressSockets[fullNamespace]
|
||||
return window.cypressSockets[fullNamespace] as unknown as SocketShape
|
||||
}
|
||||
|
||||
return io(uri, opts)
|
||||
@@ -49,8 +46,7 @@ export function createWebsocket ({ path, browserFamily }: { path: string, browse
|
||||
// Connect the socket regardless of whether or not we have newly created it
|
||||
window.cypressSockets[fullNamespace].connect()
|
||||
|
||||
// @ts-expect-error TODO: fix type
|
||||
return window.cypressSockets[fullNamespace]
|
||||
return window.cypressSockets[fullNamespace] as unknown as SocketShape
|
||||
}
|
||||
|
||||
return io({
|
||||
@@ -1,8 +1,9 @@
|
||||
/// <reference lib="dom" />
|
||||
import Emitter from 'component-emitter'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { decode, encode } from './utils'
|
||||
import type { SocketShape } from './types'
|
||||
import { decode, encode } from '../utils'
|
||||
import Emitter from 'component-emitter'
|
||||
|
||||
export type SocketShape = Emitter
|
||||
|
||||
type CDPSocketNamespaceKey = `cypressSocket-${string}`
|
||||
type CDPSendToServerNamespaceKey = `cypressSendToServer-${string}`
|
||||
7
packages/socket/lib/client/index.ts
Normal file
7
packages/socket/lib/client/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export { client, createWebsocket } from './browser'
|
||||
|
||||
export { CDPBrowserSocket } from './cdp-browser'
|
||||
|
||||
export type { Socket } from 'socket.io-client'
|
||||
|
||||
export type { SocketShape } from './cdp-browser'
|
||||
@@ -2,7 +2,7 @@ import type { CDPClient } from '@packages/types/src/protocol'
|
||||
import type Protocol from 'devtools-protocol/types/protocol.d'
|
||||
import { EventEmitter } from 'stream'
|
||||
import { randomUUID } from 'crypto'
|
||||
import { decode, encode } from './utils'
|
||||
import { decode, encode } from '../utils'
|
||||
import Debug from 'debug'
|
||||
|
||||
const debugVerbose = Debug('cypress-verbose:server:socket:cdp-socket')
|
||||
@@ -36,6 +36,7 @@ export class CDPSocketServer extends EventEmitter {
|
||||
|
||||
// @ts-expect-error TODO: fix emit type
|
||||
emit = async (event: string, ...args: any[]) => {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this._cdpSocket?.emit(event, ...args)
|
||||
|
||||
return true
|
||||
5
packages/socket/lib/node/index.ts
Normal file
5
packages/socket/lib/node/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { SocketIOServer, getPathToClientSource, getClientVersion } from './socket'
|
||||
|
||||
export type { ServerOptions, Socket, Namespace as SocketIONamespace } from 'socket.io'
|
||||
|
||||
export { CDPSocketServer } from './cdp-socket'
|
||||
@@ -1,18 +1,15 @@
|
||||
import buffer from 'buffer'
|
||||
import type http from 'http'
|
||||
import server, { Server as SocketIOBaseServer, ServerOptions, Socket, Namespace } from 'socket.io'
|
||||
|
||||
export type { Socket, Namespace as SocketIONamespace }
|
||||
import { Server as SocketIOBaseServer, ServerOptions } from 'socket.io'
|
||||
|
||||
// TODO: this will need to be updated to use an ESM version of the package
|
||||
const { version } = require('socket.io-client/package.json')
|
||||
const clientSource = require.resolve('socket.io-client/dist/socket.io.js')
|
||||
|
||||
export { ServerOptions }
|
||||
|
||||
// socket.io types are incorrect
|
||||
type PatchedServerOptions = ServerOptions & { cookie: { name: string | boolean } }
|
||||
|
||||
class SocketIOServer extends SocketIOBaseServer {
|
||||
export class SocketIOServer extends SocketIOBaseServer {
|
||||
constructor (srv: http.Server, opts?: Partial<PatchedServerOptions>) {
|
||||
opts = opts ?? {}
|
||||
|
||||
@@ -27,12 +24,6 @@ class SocketIOServer extends SocketIOBaseServer {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
server,
|
||||
SocketIOServer,
|
||||
}
|
||||
|
||||
// TODO: I don't know that this is used anywhere?
|
||||
export const getPathToClientSource = () => {
|
||||
return clientSource
|
||||
}
|
||||
@@ -2,18 +2,23 @@
|
||||
"name": "@packages/socket",
|
||||
"version": "0.0.0-development",
|
||||
"private": true,
|
||||
"browser": "lib/browser.ts",
|
||||
"main": "cjs/node/index.js",
|
||||
"browser": "browser/client/index.js",
|
||||
"scripts": {
|
||||
"build-prod": "tsc || echo 'built, with type errors' && rm lib/browser.js",
|
||||
"check-ts": "tsc --noEmit && yarn -s tslint",
|
||||
"clean": "rimraf --glob 'lib/*.js'",
|
||||
"build": "yarn build:browser && yarn build:node && yarn build:node:esm",
|
||||
"build-prod": "yarn build",
|
||||
"build:browser": "rimraf browser && tsc -p tsconfig.browser.json",
|
||||
"build:node": "rimraf cjs && tsc -p tsconfig.cjs.json",
|
||||
"build:node:esm": "rimraf esm && tsc -p tsconfig.esm.json",
|
||||
"check-ts": "tsc -p tsconfig.browser.json --noEmit && tsc -p tsconfig.cjs.json --noEmit && yarn -s tslint",
|
||||
"clean-deps": "rimraf node_modules",
|
||||
"postinstall": "patch-package",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, .",
|
||||
"test": "yarn test-unit",
|
||||
"test-debug": "vitest --inspect-brk --no-file-parallelism --test-timeout=0 --hook-timeout=0",
|
||||
"test-unit": "vitest",
|
||||
"tslint": "tslint --config ../ts/tslint.json --project ."
|
||||
"tslint": "tslint --config ../ts/tslint.json --project .",
|
||||
"watch": "yarn build:browser -- -w & yarn build:node -- -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": "1.3.0",
|
||||
@@ -32,14 +37,16 @@
|
||||
"cross-env": "7.0.3",
|
||||
"devtools-protocol": "0.0.1528500",
|
||||
"resolve-pkg": "2.0.0",
|
||||
"rimraf": "6.0.1",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib",
|
||||
"browser/",
|
||||
"cjs/",
|
||||
"esm/",
|
||||
"patches"
|
||||
],
|
||||
"types": "lib/socket.ts",
|
||||
"module": "esm/node/index.js",
|
||||
"workspaces": {
|
||||
"nohoist": [
|
||||
"devtools-protocol",
|
||||
|
||||
@@ -3,19 +3,14 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import parser from 'socket.io-parser'
|
||||
import { hasBinary } from 'socket.io-parser/dist/is-binary'
|
||||
// @ts-expect-error
|
||||
import pkg from '../package.json'
|
||||
import lib from '../index'
|
||||
import * as browserLib from '../lib/browser'
|
||||
import * as lib from '../lib/node'
|
||||
import * as browserLib from '../lib/client'
|
||||
import resolvePkg from 'resolve-pkg'
|
||||
|
||||
const { PacketType } = parser
|
||||
|
||||
describe('Socket', function () {
|
||||
it('exports server', function () {
|
||||
expect(lib.server).toBeDefined()
|
||||
})
|
||||
|
||||
it('exports client from lib/browser', function () {
|
||||
expect(browserLib.client).toBeDefined()
|
||||
})
|
||||
|
||||
18
packages/socket/tsconfig.browser.json
Normal file
18
packages/socket/tsconfig.browser.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"include": [
|
||||
"lib/client/**/*.ts",
|
||||
"lib/utils.ts"
|
||||
],
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDirs": [
|
||||
"./lib/client",
|
||||
"./lib/utils"
|
||||
],
|
||||
"outDir": "./browser",
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
18
packages/socket/tsconfig.cjs.json
Normal file
18
packages/socket/tsconfig.cjs.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"include": [
|
||||
"lib/node/**/*.ts",
|
||||
"lib/utils.ts"
|
||||
],
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDirs": [
|
||||
"./lib/node",
|
||||
"./lib/utils"
|
||||
],
|
||||
"outDir": "./cjs",
|
||||
"target": "ES2022",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
19
packages/socket/tsconfig.esm.json
Normal file
19
packages/socket/tsconfig.esm.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"include": [
|
||||
"lib/node/**/*.ts",
|
||||
"lib/utils.ts"
|
||||
],
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDirs": [
|
||||
"./lib/node",
|
||||
"./lib/utils"
|
||||
],
|
||||
"outDir": "./esm",
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"extends": "./../ts/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"skipLibCheck": false
|
||||
},
|
||||
"include": [
|
||||
"lib/*.ts",
|
||||
"lib/**/*.ts"
|
||||
],
|
||||
"files": [
|
||||
"./../ts/index.d.ts"
|
||||
]
|
||||
}
|
||||
"compilerOptions": {
|
||||
"allowJs": false,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": false,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { expect } = require('chai')
|
||||
const HttpsProxyAgent = require('https-proxy-agent')
|
||||
const os = require('os')
|
||||
const socketIo = require('@packages/socket/lib/browser')
|
||||
const socketIo = require('@packages/socket/browser/client')
|
||||
|
||||
module.exports = {
|
||||
'e2e': {
|
||||
|
||||
@@ -821,6 +821,7 @@
|
||||
"./packages/server/lib/cloud/studio/StudioLifecycleManager.ts",
|
||||
"./packages/server/lib/cloud/user.ts",
|
||||
"./packages/server/lib/config.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/cypress.ts",
|
||||
"./packages/server/lib/environment.js",
|
||||
"./packages/server/lib/modes/record.ts",
|
||||
@@ -899,9 +900,8 @@
|
||||
"./packages/server/node_modules/tough-cookie/lib/cookie.js",
|
||||
"./packages/server/start-cypress.js",
|
||||
"./packages/server/v8-snapshot-entry.js",
|
||||
"./packages/socket/index.js",
|
||||
"./packages/socket/lib/cdp-socket.ts",
|
||||
"./packages/socket/lib/socket.ts",
|
||||
"./packages/socket/cjs/node/cdp-socket.js",
|
||||
"./packages/socket/cjs/node/socket.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/browser.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/index.js",
|
||||
"./packages/socket/node_modules/socket.io/dist/broadcast-operator.js",
|
||||
@@ -4091,7 +4091,6 @@
|
||||
"./packages/server/lib/cloud/upload/stream_activity_monitor.ts",
|
||||
"./packages/server/lib/cloud/upload/stream_stalled_error.ts",
|
||||
"./packages/server/lib/cohorts.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/controllers/files.ts",
|
||||
"./packages/server/lib/controllers/iframes.ts",
|
||||
"./packages/server/lib/controllers/runner.ts",
|
||||
@@ -4289,7 +4288,8 @@
|
||||
"./packages/server/node_modules/whatwg-url/lib/public-api.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/url-state-machine.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/utils.js",
|
||||
"./packages/socket/lib/utils.ts",
|
||||
"./packages/socket/cjs/node/index.js",
|
||||
"./packages/socket/cjs/utils.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/commons.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/decodePacket.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/encodePacket.js",
|
||||
|
||||
@@ -820,6 +820,7 @@
|
||||
"./packages/server/lib/cloud/studio/StudioLifecycleManager.ts",
|
||||
"./packages/server/lib/cloud/user.ts",
|
||||
"./packages/server/lib/config.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/cypress.ts",
|
||||
"./packages/server/lib/environment.js",
|
||||
"./packages/server/lib/modes/record.ts",
|
||||
@@ -898,9 +899,8 @@
|
||||
"./packages/server/node_modules/tough-cookie/lib/cookie.js",
|
||||
"./packages/server/start-cypress.js",
|
||||
"./packages/server/v8-snapshot-entry.js",
|
||||
"./packages/socket/index.js",
|
||||
"./packages/socket/lib/cdp-socket.ts",
|
||||
"./packages/socket/lib/socket.ts",
|
||||
"./packages/socket/cjs/node/cdp-socket.js",
|
||||
"./packages/socket/cjs/node/socket.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/browser.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/index.js",
|
||||
"./packages/socket/node_modules/socket.io/dist/broadcast-operator.js",
|
||||
@@ -4090,7 +4090,6 @@
|
||||
"./packages/server/lib/cloud/upload/stream_activity_monitor.ts",
|
||||
"./packages/server/lib/cloud/upload/stream_stalled_error.ts",
|
||||
"./packages/server/lib/cohorts.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/controllers/files.ts",
|
||||
"./packages/server/lib/controllers/iframes.ts",
|
||||
"./packages/server/lib/controllers/runner.ts",
|
||||
@@ -4288,7 +4287,8 @@
|
||||
"./packages/server/node_modules/whatwg-url/lib/public-api.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/url-state-machine.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/utils.js",
|
||||
"./packages/socket/lib/utils.ts",
|
||||
"./packages/socket/cjs/node/index.js",
|
||||
"./packages/socket/cjs/utils.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/commons.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/decodePacket.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/encodePacket.js",
|
||||
|
||||
@@ -827,6 +827,7 @@
|
||||
"./packages/server/lib/cloud/studio/StudioLifecycleManager.ts",
|
||||
"./packages/server/lib/cloud/user.ts",
|
||||
"./packages/server/lib/config.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/cypress.ts",
|
||||
"./packages/server/lib/environment.js",
|
||||
"./packages/server/lib/modes/record.ts",
|
||||
@@ -905,9 +906,8 @@
|
||||
"./packages/server/node_modules/tough-cookie/lib/cookie.js",
|
||||
"./packages/server/start-cypress.js",
|
||||
"./packages/server/v8-snapshot-entry.js",
|
||||
"./packages/socket/index.js",
|
||||
"./packages/socket/lib/cdp-socket.ts",
|
||||
"./packages/socket/lib/socket.ts",
|
||||
"./packages/socket/cjs/node/cdp-socket.js",
|
||||
"./packages/socket/cjs/node/socket.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/browser.js",
|
||||
"./packages/socket/node_modules/socket.io-parser/node_modules/debug/src/index.js",
|
||||
"./packages/socket/node_modules/socket.io/dist/broadcast-operator.js",
|
||||
@@ -4090,7 +4090,6 @@
|
||||
"./packages/server/lib/cloud/upload/stream_activity_monitor.ts",
|
||||
"./packages/server/lib/cloud/upload/stream_stalled_error.ts",
|
||||
"./packages/server/lib/cohorts.ts",
|
||||
"./packages/server/lib/controllers/client.ts",
|
||||
"./packages/server/lib/controllers/files.ts",
|
||||
"./packages/server/lib/controllers/iframes.ts",
|
||||
"./packages/server/lib/controllers/runner.ts",
|
||||
@@ -4288,7 +4287,8 @@
|
||||
"./packages/server/node_modules/whatwg-url/lib/public-api.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/url-state-machine.js",
|
||||
"./packages/server/node_modules/whatwg-url/lib/utils.js",
|
||||
"./packages/socket/lib/utils.ts",
|
||||
"./packages/socket/cjs/node/index.js",
|
||||
"./packages/socket/cjs/utils.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/commons.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/decodePacket.js",
|
||||
"./packages/socket/node_modules/engine.io-parser/lib/encodePacket.js",
|
||||
|
||||
78
yarn.lock
78
yarn.lock
@@ -3047,6 +3047,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||
|
||||
"@discoveryjs/json-ext@^0.6.1":
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83"
|
||||
integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==
|
||||
|
||||
"@electron/asar@^3.2.13", "@electron/asar@^3.2.7":
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.4.1.tgz#4e9196a4b54fba18c56cd8d5cac67c5bdc588065"
|
||||
@@ -9017,6 +9022,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
|
||||
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
|
||||
|
||||
"@types/webextension-polyfill@0.12.4":
|
||||
version "0.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.12.4.tgz#d111b76e1ebf421fb64244598453bf44763a0266"
|
||||
integrity sha512-wK8YdSI0pDiaehSLDIvtvonYmLwUUivg4Z6JCJO8rkyssMAG82cFJgwPK/V7NO61mJBLg/tXeoXQL8AFzpXZmQ==
|
||||
|
||||
"@types/webpack-bundle-analyzer@4.7.0":
|
||||
version "4.7.0"
|
||||
resolved "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#fe199e724ce3d38705f6f1ba4d62429b7c360541"
|
||||
@@ -10166,16 +10176,31 @@
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646"
|
||||
integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==
|
||||
|
||||
"@webpack-cli/configtest@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-3.0.1.tgz#76ac285b9658fa642ce238c276264589aa2b6b57"
|
||||
integrity sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==
|
||||
|
||||
"@webpack-cli/info@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd"
|
||||
integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==
|
||||
|
||||
"@webpack-cli/info@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-3.0.1.tgz#3cff37fabb7d4ecaab6a8a4757d3826cf5888c63"
|
||||
integrity sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==
|
||||
|
||||
"@webpack-cli/serve@^2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e"
|
||||
integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==
|
||||
|
||||
"@webpack-cli/serve@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-3.0.1.tgz#bd8b1f824d57e30faa19eb78e4c0951056f72f00"
|
||||
integrity sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==
|
||||
|
||||
"@xmldom/xmldom@^0.8.8":
|
||||
version "0.8.10"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
|
||||
@@ -13400,6 +13425,11 @@ commander@^10.0.0, commander@^10.0.1:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
||||
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
|
||||
|
||||
commander@^12.1.0:
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
|
||||
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
|
||||
|
||||
commander@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.0.tgz#f244fc74a92343514e56229f16ef5c5e22ced5e9"
|
||||
@@ -15691,11 +15721,16 @@ env-paths@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da"
|
||||
integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==
|
||||
|
||||
envinfo@7.13.0, envinfo@^7.7.3:
|
||||
envinfo@7.13.0:
|
||||
version "7.13.0"
|
||||
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31"
|
||||
integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==
|
||||
|
||||
envinfo@^7.14.0, envinfo@^7.7.3:
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.18.0.tgz#38793d9dab9a5dec7b2a3146ed094cda8e754ed8"
|
||||
integrity sha512-02QGCLRW+Jb8PC270ic02lat+N57iBaWsvHjcJViqp6UVupRB+Vsg7brYPTqEFXvsdTql3KnSczv5ModZFpl8Q==
|
||||
|
||||
environment@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1"
|
||||
@@ -15727,11 +15762,6 @@ enzyme-adapter-utils@^1.11.0:
|
||||
prop-types "^15.7.2"
|
||||
semver "^5.7.1"
|
||||
|
||||
eol@0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/eol/-/eol-0.10.0.tgz#51b35c6b9aa0329a26d102b6ddc454be8654739b"
|
||||
integrity sha512-+w3ktYrOphcIqC1XKmhQYvM+o2uxgQFiimL7B6JPZJlWVxf7Lno9e/JWLPIgbHo7DoZ+b7jsf/NzrUcNe6ZTZQ==
|
||||
|
||||
err-code@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
|
||||
@@ -33303,6 +33333,25 @@ webpack-cli@^5.1.4:
|
||||
rechoir "^0.8.0"
|
||||
webpack-merge "^5.7.3"
|
||||
|
||||
webpack-cli@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207"
|
||||
integrity sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.6.1"
|
||||
"@webpack-cli/configtest" "^3.0.1"
|
||||
"@webpack-cli/info" "^3.0.1"
|
||||
"@webpack-cli/serve" "^3.0.1"
|
||||
colorette "^2.0.14"
|
||||
commander "^12.1.0"
|
||||
cross-spawn "^7.0.3"
|
||||
envinfo "^7.14.0"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
import-local "^3.0.2"
|
||||
interpret "^3.1.1"
|
||||
rechoir "^0.8.0"
|
||||
webpack-merge "^6.0.1"
|
||||
|
||||
webpack-dev-middleware@^7.4.2:
|
||||
version "7.4.2"
|
||||
resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz#40e265a3d3d26795585cff8207630d3a8ff05877"
|
||||
@@ -33367,6 +33416,15 @@ webpack-merge@^5.4.0, webpack-merge@^5.7.3:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-merge@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a"
|
||||
integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==
|
||||
dependencies:
|
||||
clone-deep "^4.0.1"
|
||||
flat "^5.0.2"
|
||||
wildcard "^2.0.1"
|
||||
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
@@ -33629,10 +33687,10 @@ widest-line@^4.0.1:
|
||||
dependencies:
|
||||
string-width "^5.0.1"
|
||||
|
||||
wildcard@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
|
||||
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
|
||||
wildcard@^2.0.0, wildcard@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
|
||||
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
|
||||
|
||||
win-version-info@^6.0.1:
|
||||
version "6.0.1"
|
||||
|
||||
Reference in New Issue
Block a user