refactor: Data context cleanup & IPC bindings for data push (#18357)

This commit is contained in:
Tim Griesser
2021-10-11 12:13:41 -04:00
committed by GitHub
parent b07ccf8972
commit d841e1331f
120 changed files with 1211 additions and 814 deletions

22
.gitignore vendored
View File

@@ -13,18 +13,35 @@ cypress.zip
Cached Theme.pak
Cached Theme Material Design.pak
# from data-context, compiled .js files
packages/data-context/src/**/*.js
# from desktop-gui
packages/desktop-gui/cypress/videos
packages/desktop-gui/src/jsconfig.json
# from driver
packages/driver/cypress/videos
packages/driver/cypress/screenshots
# from launcher, compiled .js files
packages/launcher/index.js
packages/launcher/lib/**/*.js
# from network, compiled .js files
packages/network/lib/**/*.js
# from net-stubbing, compiled .js files
packages/net-stubbing/lib/**/*.js
# from runner
packages/runner/cypress/videos
packages/runner/cypress/screenshots
# from proxy, compiled .js files
packages/proxy/lib/**/*.js
# npm packages
npm/**/cypress/screenshots
@@ -40,6 +57,9 @@ packages/server/support
packages/server/test/support/fixtures/server/imgs
packages/server/test/support/fixtures/server/libs
# from socket, dist built files
packages/socket/lib/*.js
# from npm/react
/npm/react/bin/*
/npm/react/cypress/videos
@@ -347,4 +367,4 @@ globbed_node_modules
# Autogenerated files, typically from graphql-code-generator
*.gen.ts
*.gen.json
*.gen.json

View File

@@ -59,7 +59,7 @@
"semantic-release": "17.4.2",
"to-string-loader": "1.1.6",
"ts-loader": "8.1.0",
"ts-node": "9.1.1",
"ts-node": "^10.2.1",
"tslib": "^2.2.0",
"tslint": "5.20.1",
"typescript": "4.2.4",

View File

@@ -79,7 +79,7 @@
"sass-loader": "10.1.1",
"style-loader": "^2.0.0",
"svg-url-loader": "3.0.3",
"ts-node": "^9.1.1",
"ts-node": "^10.2.1",
"tsc-alias": "^1.2.9",
"tsconfig-paths-webpack-plugin": "^3.5.1",
"typed-scss-modules": "^4.1.1",

View File

@@ -57,7 +57,7 @@
"sinon": "^9.0.0",
"sinon-chai": "^3.5.0",
"snap-shot-it": "7.9.2",
"ts-node": "8.10.1",
"ts-node": "^10.2.1",
"webpack": "^4.41.12"
},
"peerDependencies": {

View File

@@ -32,6 +32,8 @@
"cypress:run:debug": "node ./scripts/debug.js cypress:run",
"cypress:verify": "cypress verify --dev",
"dev": "gulp dev",
"dev:no-watch": "gulp dev:no-watch",
"dev:clean": "gulp dev:clean",
"gulp:debug": "node --inspect-brk ./node_modules/.bin/gulp",
"dev-debug": "node ./scripts/debug.js dev",
"docker": "./scripts/run-docker-local.sh",
@@ -116,6 +118,7 @@
"@types/react": "16.9.50",
"@types/react-dom": "16.9.8",
"@types/request-promise": "4.1.45",
"@types/send": "^0.17.1",
"@types/sinon-chai": "3.2.3",
"@types/through2": "^2.0.36",
"@typescript-eslint/eslint-plugin": "4.18.0",
@@ -129,6 +132,7 @@
"babel-eslint": "10.1.0",
"bluebird": "3.5.3",
"bluebird-retry": "0.11.0",
"c8": "^7.10.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"chalk": "2.4.2",
@@ -217,7 +221,7 @@
"through": "2.3.8",
"through2": "^4.0.2",
"tree-kill": "1.2.2",
"ts-node": "8.3.0",
"ts-node": "^10.2.1",
"typescript": "^4.2.3",
"yarn-deduplicate": "3.1.0"
},

View File

@@ -1,6 +1,5 @@
{
"projectId": "sehy69",
"baseUrl": "http://localhost:5556",
"viewportWidth": 800,
"viewportHeight": 850,
"fixturesFolder": false,
@@ -14,11 +13,16 @@
"reporterOptions": {
"configFile": "../../mocha-reporter-config.json"
},
"integrationFolder": "cypress/e2e/integration",
"componentFolder": "src",
"supportFile": false,
"component": {
"testFiles": "**/*.spec.{js,ts,tsx,jsx}",
"supportFile": "cypress/component/support/index.ts",
"pluginsFile": "cypress/component/plugins/index.js"
},
"e2e": {
"pluginsFile": "cypress/e2e/plugins/index.ts",
"supportFile": "cypress/e2e/support/e2eSupport.ts"
}
}

View File

@@ -0,0 +1,39 @@
let GQL_PORT
let SERVER_PORT
describe('App', () => {
beforeEach(() => {
cy.withCtx(async (ctx) => {
await ctx.dispose()
await ctx.actions.project.setActiveProject(ctx.launchArgs.projectRoot)
ctx.actions.wizard.setTestingType('e2e')
await ctx.actions.project.initializeActiveProject({
skipPluginIntializeForTesting: true,
})
await ctx.actions.project.launchProject({
skipBrowserOpenForTest: true,
})
return [
ctx.gqlServerPort,
ctx.appServerPort,
]
}).then(([gqlPort, serverPort]) => {
GQL_PORT = gqlPort
SERVER_PORT = serverPort
})
})
it('resolves the home page', () => {
cy.visit(`dist/index.html?serverPort=${SERVER_PORT}&gqlPort=${GQL_PORT}`)
cy.get('[href="#/runner"]').click()
cy.get('[href="#/settings"]').click()
})
it('resolves the home page, with a different server port?', () => {
cy.visit(`dist/index.html?serverPort=${SERVER_PORT}&gqlPort=${GQL_PORT}`)
cy.get('[href="#/runner"]').click()
cy.get('[href="#/settings"]').click()
})
})

View File

@@ -1,4 +1,7 @@
/// <reference types="cypress" />
const { monorepoPaths } = require('../../../../../scripts/gulp/monorepoPaths')
import { e2ePluginSetup } from '@packages/frontend-shared/cypress/e2e/e2ePluginSetup'
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
@@ -15,8 +18,6 @@
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
module.exports = async (on, config) => {
return await e2ePluginSetup(monorepoPaths.pkgApp, on, config)
}

View File

@@ -0,0 +1,2 @@
/// <reference path="../../../../frontend-shared/cypress/e2e/support/e2eSupport.ts" />
require('../../../../frontend-shared/cypress/e2e/support/e2eSupport')

View File

@@ -1,7 +0,0 @@
describe('App', () => {
it('resolves the home page', () => {
cy.visit('http://localhost:5556')
cy.get('[data-e2e-href="/runner"]').click()
cy.get('[data-e2e-href="/settings"]').click()
})
})

View File

@@ -14,10 +14,7 @@
"debug": "gulp debug --project ${PWD}",
"dev": "gulp dev --project ${PWD}",
"start": "echo \"run 'yarn dev' from the root\" && exit 1",
"watch": "echo \"run 'yarn dev' from the root\" && exit 1",
"vite:dev": "vite",
"vite:build": "vite build",
"vite:preview": "yarn vite:build && vite preview"
"watch": "echo \"run 'yarn dev' from the root\" && exit 1"
},
"dependencies": {},
"devDependencies": {
@@ -76,13 +73,25 @@
"vite": {
"optimizeDeps": {
"include": [
"@iconify/iconify",
"@testing-library/cypress/add-commands",
"@urql/exchange-execute",
"@urql/vue",
"@vueuse/core",
"cypress-file-upload",
"dedent",
"fake-uuid",
"graphql",
"graphql-relay",
"graphql/jsutils/Path",
"lodash",
"mobx",
"nanoid",
"path",
"socket.io-client",
"vue",
"vue-toast-notification"
"vue-toast-notification",
"wonka"
]
}
}

View File

@@ -1,5 +0,0 @@
describe('hello', () => {
it('works', () => {
expect(1).to.eq(1)
})
})

View File

@@ -9,7 +9,7 @@ import { createRouter } from './router/router'
const app = createApp(App)
app.use(urql, makeUrqlClient())
app.use(urql, makeUrqlClient('app'))
app.use(createRouter())
app.use(createI18n())

View File

@@ -25,10 +25,10 @@
</route>
<script lang="ts" setup>
import { gql, useMutation, useQuery } from '@urql/vue'
import { MainQueryDocument, GenerateSpecFromStoryDocument } from '../generated/graphql'
import { NewSpecQueryDocument, NewSpec_GenerateSpecFromStoryDocument } from '../generated/graphql'
gql`
query MainQuery {
query NewSpecQuery {
wizard {
storybook {
configured
@@ -39,7 +39,7 @@ query MainQuery {
`
gql`
mutation GenerateSpecFromStory($storyPath: String!) {
mutation NewSpec_GenerateSpecFromStory($storyPath: String!) {
generateSpecFromStory (storyPath: $storyPath) {
storybook {
configured,
@@ -49,8 +49,8 @@ mutation GenerateSpecFromStory($storyPath: String!) {
}
`
const query = useQuery({ query: MainQueryDocument })
const mutation = useMutation(GenerateSpecFromStoryDocument)
const query = useQuery({ query: NewSpecQueryDocument })
const mutation = useMutation(NewSpec_GenerateSpecFromStoryDocument)
async function storyClick (story) {
await mutation.executeMutation({ storyPath: story })

View File

@@ -1,15 +1,12 @@
import { createRouter as _createRouter, createWebHistory } from 'vue-router'
import { createRouter as _createRouter, createWebHashHistory } from 'vue-router'
import generatedRoutes from 'virtual:generated-pages'
import { setupLayouts } from 'virtual:generated-layouts'
export const createRouter = () => {
const routes = setupLayouts(generatedRoutes)
// TODO: clean this up
const historyBase = window.location.href.includes('__vite__') ? '__vite__' : ''
return _createRouter({
history: createWebHistory(historyBase),
history: createWebHashHistory(),
routes,
})
}

View File

@@ -8,7 +8,7 @@
"build-prod": "tsc || echo 'built, with errors'",
"check-ts": "tsc --noEmit",
"clean-deps": "rimraf node_modules",
"clean": "rimraf ./src/*.js ./src/**/*.js ./src/**/**/*.js ./test/**/*.js || echo 'cleaned'",
"clean": "rimraf './{src,test}/**/*.js'",
"test-unit": "mocha -r @packages/ts/register test/unit/**/*.spec.ts --config ./test/.mocharc.js --exit",
"test-integration": "mocha -r @packages/ts/register test/integration/**/*.spec.ts --config ./test/.mocharc.js --exit"
},
@@ -22,5 +22,8 @@
"mocha": "7.0.1",
"rimraf": "3.0.2"
},
"files": [
"src"
],
"types": "src/index.ts"
}

View File

@@ -1,4 +1,4 @@
import type { LaunchArgs, OpenProjectLaunchOptions } from '@packages/types'
import type { LaunchArgs, OpenProjectLaunchOptions, PlatformName } from '@packages/types'
import type { AppApiShape, ProjectApiShape } from './actions'
import type { NexusGenAbstractTypeMembers } from '@packages/graphql/src/gen/nxs.gen'
import type { AuthApiShape } from './actions/AuthActions'
@@ -17,8 +17,10 @@ import {
StorybookDataSource,
} from './sources/'
import { cached } from './util/cached'
import { DataContextShell, DataContextShellConfig } from './DataContextShell'
export interface DataContextConfig {
export interface DataContextConfig extends DataContextShellConfig {
os: PlatformName
launchArgs: LaunchArgs
launchOptions: OpenProjectLaunchOptions
/**
@@ -33,15 +35,39 @@ export interface DataContextConfig {
projectApi: ProjectApiShape
}
export class DataContext {
export class DataContext extends DataContextShell {
private _coreData: CoreDataShape
fs = fsExtra
@cached
get fs () {
return fsExtra
}
constructor (private config: DataContextConfig) {
super(config)
this._coreData = config.coreData ?? makeCoreData()
}
async initializeData () {
const toAwait: Promise<any>[] = [
// Fetch the browsers when the app starts, so we have some by
// the time we're continuing.
this.actions.app.refreshBrowsers(),
// load projects from cache on start
this.actions.project.loadProjects(),
]
if (this.config.launchArgs.projectRoot) {
toAwait.push(this.actions.project.setActiveProject(this.config.launchArgs.projectRoot))
}
return Promise.all(toAwait)
}
get os () {
return this.config.os
}
get launchArgs () {
return this.config.launchArgs
}
@@ -132,6 +158,7 @@ export class DataContext {
appApi: this.config.appApi,
authApi: this.config.authApi,
projectApi: this.config.projectApi,
busApi: this.config.rootBus,
}
}
@@ -160,8 +187,11 @@ export class DataContext {
console.error(e)
}
dispose () {
this.util.disposeLoaders()
async dispose () {
return Promise.all([
this.util.disposeLoaders(),
this.actions.project.clearActiveProject(),
])
}
get loader () {

View File

@@ -0,0 +1,43 @@
import { EventEmitter } from 'events'
import { DataEmitterActions } from './actions/DataEmitterActions'
import { cached } from './util/cached'
export interface DataContextShellConfig {
rootBus: EventEmitter
}
// Used in places where we have to create a "shell" data context,
// for non-unified parts of the codebase
export class DataContextShell {
private _appServerPort: number | undefined
private _gqlServerPort: number | undefined
constructor (private shellConfig: DataContextShellConfig = { rootBus: new EventEmitter }) {}
setAppServerPort (port: number | undefined) {
this._appServerPort = port
}
setGqlServerPort (port: number | undefined) {
this._gqlServerPort = port
}
get appServerPort () {
return this._appServerPort
}
get gqlServerPort () {
return this._gqlServerPort
}
@cached
get emitter () {
return new DataEmitterActions(this)
}
get _apis () {
return {
busApi: this.shellConfig.rootBus,
}
}
}

View File

@@ -0,0 +1,29 @@
import type { SocketIOServer } from '@packages/socket'
import type { DataContextShell } from '../DataContextShell'
export class DataEmitterActions {
private _launchpadSocketServer: SocketIOServer | undefined
private _appSocketServer: SocketIOServer | undefined
constructor (private ctx: DataContextShell) {}
setLaunchpadSocketServer (socketServer: SocketIOServer | undefined) {
this._launchpadSocketServer = socketServer
}
setAppSocketServer (socketServer: SocketIOServer | undefined) {
this._appSocketServer = socketServer
}
init () {
this.ctx._apis.busApi.on('menu:item:clicked', (logout) => {
})
}
toApp (...args: any[]) {
this._appSocketServer?.emit('data-context-push', ...args)
}
toLaunchpad (ev: string, ...args: any[]) {
this._launchpadSocketServer?.emit('data-context-push', ...args)
}
}

View File

@@ -19,6 +19,7 @@ export interface ProjectApiShape {
removeProjectFromCache(projectRoot: string): void
getProjectRootsFromCache(): Promise<string[]>
clearLatestProjectsCache(): Promise<unknown>
closeActiveProject(): Promise<unknown>
}
export class ProjectActions {
@@ -28,8 +29,9 @@ export class ProjectActions {
return this.ctx._apis.projectApi
}
clearActiveProject () {
async clearActiveProject () {
this.ctx.appData.activeProject = null
await this.api.closeActiveProject()
return
}
@@ -98,8 +100,8 @@ export class ProjectActions {
return this.projects
}
async initializeActiveProject () {
if (!this.ctx.activeProject?.projectRoot || !this.ctx.wizardData.chosenTestingType) {
async initializeActiveProject (options: OpenProjectLaunchOptions = {}) {
if (!this.ctx.activeProject?.projectRoot) {
throw Error('Cannot initialize project without an active project')
}
@@ -115,7 +117,18 @@ export class ProjectActions {
testingType: this.ctx.wizardData.chosenTestingType,
}
await this.api.initializeProject(launchArgs, this.ctx.launchOptions, browsers)
try {
await this.api.initializeProject(launchArgs, {
...this.ctx.launchOptions,
...options,
ctx: this.ctx,
}, browsers)
} catch (e) {
// TODO(tim): remove / replace with ctx.log.error
// eslint-disable-next-line
console.error(e)
throw e
}
}
createProject () {
@@ -153,7 +166,7 @@ export class ProjectActions {
}
}
async launchProject () {
async launchProject (options: LaunchOpts = {}) {
const browser = this.ctx.wizardData.chosenBrowser ?? this.ctx.appData.browsers?.[0]
if (!browser) {
@@ -167,7 +180,7 @@ export class ProjectActions {
specType: this.ctx.wizardData.chosenTestingType === 'e2e' ? 'integration' : 'component',
}
return this.api.launchProject(browser, spec, {})
return this.api.launchProject(browser, spec, options)
}
removeProject (projectRoot: string) {

View File

@@ -3,6 +3,7 @@
export * from './AppActions'
export * from './AuthActions'
export * from './DataEmitterActions'
export * from './FileActions'
export * from './ProjectActions'
export * from './StorybookActions'

View File

@@ -1,5 +0,0 @@
export class DataEmitter {
//
}

View File

@@ -1,5 +1,4 @@
/* eslint-disable padding-line-between-statements */
// created by autobarrel, do not modify directly
export * from './DataEmitter'
export * from './coreDataShape'

View File

@@ -1,6 +1,7 @@
export type {
export {
DataContext,
DataContextConfig,
} from './DataContext'
export * from './makeDataContext'
export type {
DataContextConfig,
} from './DataContext'

View File

@@ -1,14 +0,0 @@
import { DataContext, DataContextConfig } from './DataContext'
/**
* Creates & initializes a "Data Context" instance,
*/
export async function makeDataContext (config: DataContextConfig) {
const ctx = new DataContext(config)
if (config.launchArgs.projectRoot) {
await ctx.actions.project.setActiveProject(config.launchArgs.projectRoot)
}
return ctx
}

View File

@@ -7,7 +7,6 @@
],
"compilerOptions": {
"lib": ["es2020"],
"importHelpers": true,
"strict": true,
"allowJs": false,
"noImplicitAny": true,

View File

@@ -2,6 +2,7 @@
"extends": "./../ts/tsconfig.json",
"compilerOptions": {
"target": "ES2016",
"lib": ["ES2018", "DOM", "DOM.Iterable"],
"module": "commonjs",
"allowJs": true,
"noImplicitAny": false,

View File

@@ -119,8 +119,9 @@ module.exports = {
// we have an active debugger session
if (inspector.url()) {
const dp = process.debugPort + 1
const inspectFlag = process.execArgv.includes('--inspect') ? '--inspect' : '--inspect-brk'
argv.unshift(`--inspect-brk=${dp}`)
argv.unshift(`${inspectFlag}=${dp}`)
} else {
const opts = minimist(argv)

View File

@@ -17,6 +17,6 @@
"componentFolder": "src/components"
},
"e2e": {
"supportFile": false
"supportFile": "cypress/e2e/support/e2eSupport.ts"
}
}

View File

@@ -0,0 +1,31 @@
import type { DataContext } from '@packages/data-context'
export async function e2ePluginSetup (projectRoot: string, on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) {
// require'd so we don't import the types from @packages/server which would
// pollute strict type checking
const { runInternalServer } = require('@packages/server/lib/modes/internal-server')
process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF = 'true'
const { serverPortPromise, ctx } = runInternalServer({
projectRoot,
}) as {ctx: DataContext, serverPortPromise: Promise<number>}
on('task', {
async withCtx (fnString: string) {
await serverPortPromise
return new Function('ctx', `return (${fnString})(ctx)`).call(undefined, ctx)
},
async resetCtxState () {
return ctx.dispose()
},
getGraphQLPort () {
return serverPortPromise
},
getAppServerPort () {
return ctx.appServerPort ?? null
},
})
return config
}

View File

@@ -0,0 +1,27 @@
import type { DataContext } from '@packages/data-context'
const SIXTY_SECONDS = 60 * 1000
declare global {
namespace Cypress {
interface Chainable {
withCtx(fn: (ctx: DataContext) => any): Chainable
}
}
}
Cypress.Commands.add('withCtx', (fn) => {
const _log = Cypress.log({
name: 'withCtx',
message: '(view in console)',
consoleProps () {
return {
'Executed': fn.toString(),
}
},
})
cy.task('withCtx', fn.toString(), { timeout: SIXTY_SECONDS, log: false }).then(() => {
_log.end()
})
})

View File

@@ -1,5 +1,5 @@
import _ from 'lodash'
import { randomComponents } from '../../../src/graphql/specs/testStubSpecs'
import { randomComponents } from './testStubSpecs'
import config from '../../fixtures/config.json'
import type {

View File

@@ -1,4 +0,0 @@
// Unsure why the ImportMeta type isn't picking this up.
interface ImportMetaEnv {
VITE_CYPRESS_INTERNAL_GQL_PORT: string
}

View File

@@ -3,18 +3,22 @@ import {
createClient,
dedupExchange,
errorExchange,
Exchange,
fetchExchange,
Exchange,
} from '@urql/core'
import { devtoolsExchange } from '@urql/devtools'
import VueToast, { ToastPluginApi } from 'vue-toast-notification'
import { client } from '@packages/socket/lib/browser'
import 'vue-toast-notification/dist/theme-sugar.css'
export { VueToast }
import { cacheExchange as graphcacheExchange } from '@urql/exchange-graphcache'
import { pubSubExchange } from './urqlExchangePubsub'
import { GRAPHQL_URL } from '../utils/env'
const GQL_PORT_MATCH = /gqlPort=(\d+)/.exec(window.location.search)
const SERVER_PORT_MATCH = /serverPort=(\d+)/.exec(window.location.search)
declare global {
interface Window {
@@ -32,9 +36,30 @@ export function makeCacheExchange () {
})
}
export function makeUrqlClient (): Client {
export function makeUrqlClient (target: 'launchpad' | 'app'): Client {
let gqlPort: string
if (GQL_PORT_MATCH) {
gqlPort = GQL_PORT_MATCH[1]
} else {
// @ts-ignore
gqlPort = window.__CYPRESS_GRAPHQL_PORT__
}
if (!gqlPort) {
throw new Error(`${window.location.href} cannot be visited without a gqlPort`)
}
const GRAPHQL_URL = `http://localhost:${gqlPort}/graphql`
// If we're in the launchpad, we connect to the known GraphQL Socket port,
// otherwise we connect to the /__socket.io of the current domain, unless we've explicitly
//
const io = getPubSubSource({ target, gqlPort, serverPort: SERVER_PORT_MATCH?.[1] })
const exchanges: Exchange[] = [
dedupExchange,
pubSubExchange(io),
errorExchange({
onError (error) {
const message = `
@@ -53,6 +78,9 @@ export function makeUrqlClient (): Client {
}),
// https://formidable.com/open-source/urql/docs/graphcache/errors/
makeCacheExchange(),
// TODO(tim): add this when we want to use the socket as the GraphQL
// transport layer for all operations
// target === 'launchpad' ? fetchExchange : socketExchange(io),
fetchExchange,
]
@@ -66,3 +94,40 @@ export function makeUrqlClient (): Client {
exchanges,
})
}
interface PubSubConfig {
target: 'launchpad' | 'app'
gqlPort: string
serverPort?: string
}
function getPubSubSource (config: PubSubConfig) {
if (config.target === 'launchpad') {
return client(`http://localhost:${config.gqlPort}`, {
path: '/__gqlSocket',
transports: ['websocket'],
})
}
// Only happens during testing
if (config.serverPort) {
return client(`http://localhost:${config.serverPort}`, {
path: '/__socket.io',
transports: ['websocket'],
})
}
return client({
path: '/__socket.io',
transports: ['websocket'],
})
}
// TODO(tim): add this when we want to use the socket as the GraphQL
// transport layer for all operations
// const socketExchange = (io: Socket): Exchange => {
// return (input) => {
// return (ops$) => {
// }
// }
// }

View File

@@ -0,0 +1,40 @@
import { pipe, tap } from 'wonka'
import type { Exchange, Operation } from '@urql/core'
import type { Socket } from '@packages/socket/lib/browser'
export const pubSubExchange = (io: Socket): Exchange => {
return ({ client, forward }) => {
return (ops$) => {
if (typeof window === 'undefined') {
return forward(ops$)
}
const watchedOperations = new Map<number, Operation>()
const observedOperations = new Map<number, number>()
io.on('data-context-push', (...args) => {
watchedOperations.forEach((op) => {
client.reexecuteOperation(
client.createRequestOperation('query', op, {
requestPolicy: 'cache-and-network',
}),
)
})
})
const processIncomingOperation = (op: Operation) => {
if (op.kind === 'query' && !observedOperations.has(op.key)) {
observedOperations.set(op.key, 1)
watchedOperations.set(op.key, op)
}
if (op.kind === 'teardown' && observedOperations.has(op.key)) {
observedOperations.delete(op.key)
watchedOperations.delete(op.key)
}
}
return forward(pipe(ops$, tap(processIncomingOperation)))
}
}
}

View File

@@ -1,6 +0,0 @@
// This code is meant to be executed within Vite
// which supports environment variables being injected into the client at build time
export const GRAPHQL_PORT = import.meta.env.VITE_CYPRESS_INTERNAL_GQL_PORT || `${52200}`
export const GRAPHQL_URL = `http://localhost:${GRAPHQL_PORT}/graphql`

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
"target": "esnext",
"target": "ES2018",
"module": "esnext",
"lib": ["dom", "ESNext"],
/*

View File

@@ -8,7 +8,7 @@
"build-prod": "tsc || echo 'built, with errors'",
"check-ts": "tsc --noEmit",
"clean-deps": "rimraf node_modules",
"clean": "rimraf ./src/*.js ./src/**/*.js ./src/**/**/*.js ./test/**/*.js || echo 'cleaned'",
"clean": "rimraf './{src,test}/**/*.js'",
"postinstall": "echo '@packages/graphql needs: yarn build'",
"test-unit": "mocha -r @packages/ts/register test/unit/**/*.spec.ts --config ./test/.mocharc.js --exit",
"test-integration": "mocha -r @packages/ts/register test/integration/**/*.spec.ts --config ./test/.mocharc.js --exit"
@@ -18,7 +18,6 @@
"@graphql-tools/delegate": "8.2.1",
"@graphql-tools/utils": "8.2.3",
"@graphql-tools/wrap": "8.1.1",
"cors": "2.8.5",
"cross-fetch": "^3.1.4",
"dedent": "^0.7.0",
"express": "4.17.1",

View File

@@ -284,6 +284,8 @@ type Mutation {
"""
initializeOpenProject: Wizard
internal_clearLatestProjectCache: Boolean
internal_triggerIpcToApp: Boolean
internal_triggerIpcToLaunchpad(msg: String!): Boolean
"""Launches project from open_project global singleton"""
launchOpenProject: App

View File

@@ -4,6 +4,27 @@ import { Wizard } from './gql-Wizard'
export const mutation = mutationType({
definition (t) {
t.field('internal_triggerIpcToLaunchpad', {
type: 'Boolean',
args: {
msg: nonNull(stringArg()),
},
resolve: (root, args, ctx) => {
ctx.emitter.toLaunchpad(args.msg)
return true
},
})
t.field('internal_triggerIpcToApp', {
type: 'Boolean',
resolve: (root, args, ctx) => {
ctx.emitter.toApp('someData')
return true
},
})
t.field('internal_clearLatestProjectCache', {
type: 'Boolean',
resolve: (source, args, ctx) => {

View File

@@ -1,82 +1,16 @@
import { graphqlHTTP } from 'express-graphql'
import express from 'express'
import Debug from 'debug'
import type { Server } from 'http'
import type { AddressInfo } from 'net'
import cors from 'cors'
import getenv from 'getenv'
import { graphqlSchema } from './schema'
import type { DataContext } from '@packages/data-context'
import type express from 'express'
const debug = Debug('cypress:server:graphql')
const GRAPHQL_PORT = getenv.int('CYPRESS_INTERNAL_GQL_PORT', 52200)
let app: ReturnType<typeof express>
let server: Server
export function closeGraphQLServer (): Promise<void | null> {
if (!server || !server.listening) {
return Promise.resolve(null)
}
return new Promise<void | null>((res, rej) => {
server.close((err) => {
if (err) {
rej(err)
}
res(null)
})
})
}
// singleton during the lifetime of the application
let dataContext: DataContext | undefined
// Injected this way, since we want to set this up where the IPC layer
// is established in the server package, which should be an independent
// layer from GraphQL
export function setDataContext (ctx: DataContext) {
dataContext = ctx
return ctx
}
export function startGraphQLServer ({ port }: { port: number } = { port: GRAPHQL_PORT }): Promise<{
server: Server
app: Express.Application
endpoint: string
}> {
app = express()
app.use(cors())
export function addGraphQLHTTP (app: ReturnType<typeof express>, context: DataContext) {
app.use('/graphql', graphqlHTTP((req) => {
if (!dataContext) {
throw new Error(`setDataContext has not been called`)
}
return {
schema: graphqlSchema,
graphiql: true,
context: dataContext,
context,
}
}))
return new Promise((resolve, reject) => {
server = app.listen(port, () => {
if (process.env.NODE_ENV === 'development') {
/* eslint-disable-next-line no-console */
console.log(`GraphQL server is running at http://localhost:${port}/graphql`)
}
const endpoint = `http://localhost:${(server.address() as AddressInfo).port}/graphql`
debug(`GraphQL Server at ${endpoint}`)
return resolve({
server,
endpoint,
app,
})
})
})
return app
}

View File

@@ -9,8 +9,6 @@
"script"
],
"compilerOptions": {
"lib": ["ES2020"],
"importHelpers": true,
"strict": true,
"allowJs": false,
"noImplicitAny": true,

View File

@@ -1,6 +1,5 @@
{
"projectId": "sehy69",
"baseUrl": "http://localhost:5555",
"viewportWidth": 800,
"viewportHeight": 850,
"retries": {

View File

@@ -1 +0,0 @@
export { longBrowsersList } from '@packages/frontend-shared/cypress/support/mock-graphql/stubgql-App'

View File

@@ -1,6 +1,14 @@
let GQL_PORT
describe('Launchpad', () => {
before(() => {
cy.task('getGraphQLPort').then((port) => {
GQL_PORT = port
})
})
it('resolves the home page', () => {
cy.visit('http://localhost:5555')
cy.visit(`dist/index.html?gqlPort=${GQL_PORT}`)
cy.get('h1').should('contain', 'Welcome')
})
})

View File

@@ -0,0 +1,23 @@
/// <reference types="cypress" />
const { monorepoPaths } = require('../../../../scripts/gulp/monorepoPaths')
import { e2ePluginSetup } from '@packages/frontend-shared/cypress/e2e/e2ePluginSetup'
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
export default async (on, config) => {
return await e2ePluginSetup(monorepoPaths.pkgLaunchpad, on, config)
}

View File

@@ -98,6 +98,7 @@
"prismjs",
"prismjs/components/prism-typescript",
"prismjs/components/prism-yaml",
"socket.io-client",
"vue",
"vue-demi",
"vue-i18n",

View File

@@ -28,7 +28,7 @@
<script lang="ts" setup>
import { gql, useQuery } from '@urql/vue'
import { MainQueryDocument } from './generated/graphql'
import { MainLaunchpadQueryDocument } from './generated/graphql'
import TestingTypeCards from './setup/TestingTypeCards.vue'
import Wizard from './setup/Wizard.vue'
import WizardHeader from './setup/WizardHeader.vue'
@@ -37,7 +37,7 @@ import GlobalPage from './global/GlobalPage.vue'
import BaseError from './error/BaseError.vue'
gql`
query MainQuery {
query MainLaunchpadQuery {
...TestingTypeCards
...Wizard
@@ -54,5 +54,5 @@ query MainQuery {
}
`
const query = useQuery({ query: MainQueryDocument })
const query = useQuery({ query: MainLaunchpadQueryDocument })
</script>

View File

@@ -9,7 +9,7 @@ import { createI18n } from '@cy/i18n'
const app = createApp(App)
app.use(VueToast)
app.use(urql, makeUrqlClient())
app.use(urql, makeUrqlClient('launchpad'))
app.use(createI18n())
window.$app = app.mount('#app')

View File

@@ -1,6 +1,6 @@
import { OpenBrowserListFragmentDoc } from '../generated/graphql-test'
import OpenBrowserList from './OpenBrowserList.vue'
import { longBrowsersList } from '../../cypress/fixtures/browsers/long-browsers-list'
import { longBrowsersList } from '@packages/frontend-shared/cypress/support/mock-graphql/stubgql-App'
const launchButtonSelector = 'button[data-testid=launch-button]'

View File

@@ -1,4 +1,4 @@
import type { Url } from 'url'
import { URL, Url } from 'url'
import debugModule from 'debug'
import minimatch from 'minimatch'
import Forge from 'node-forge'

View File

@@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"build-prod": "tsc --project .",
"clean": "rimraf lib/*.js || echo 'cleaned'",
"clean": "rimraf 'lib/**/*.js'",
"clean-deps": "rimraf node_modules",
"test": "yarn test-unit",
"test-debug": "yarn test-unit --inspect-brk=5566",

View File

@@ -9,7 +9,7 @@ import tls from 'tls'
import url from 'url'
import DebuggingProxy from '@cypress/debugging-proxy'
import request from '@cypress/request-promise'
import * as socketIo from '@packages/socket'
import * as socketIo from '@packages/socket/lib/browser'
import {
buildConnectReqHead,
createProxySock,

View File

@@ -11,6 +11,7 @@ import { InterceptResponse } from '@packages/net-stubbing'
import { PassThrough, Readable } from 'stream'
import * as rewriter from './util/rewriter'
import zlib from 'zlib'
import { URL } from 'url'
export type ResponseMiddleware = HttpMiddleware<{
/**

View File

@@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"build-prod": "tsc --project .",
"clean": "rimraf 'lib/**/*.js'",
"clean-deps": "rimraf node_modules",
"run-mocha": "mocha -r @packages/ts/register -r test/pretest.ts --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
"test": "yarn run-mocha \"test/integration/*.spec.ts\" \"test/unit/**/*.spec.ts\"",

View File

@@ -7,13 +7,7 @@ export type RunnerPkg = 'app' | 'runner' | 'runner-ct'
type FoldersWithDist = 'static' | 'driver' | RunnerPkg
export const getPathToDist = (folder: FoldersWithDist, ...args: string[]) => {
let distDir = 'dist'
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF) {
distDir = 'dist-e2e'
}
return path.join(...[__dirname, '..', '..', folder, distDir, ...args])
return path.join(...[__dirname, '..', '..', folder, 'dist', ...args])
}
export const getRunnerInjectionContents = () => {
@@ -26,18 +20,12 @@ export const getPathToIndex = (pkg: RunnerPkg) => {
return getPathToDist(pkg, 'index.html')
}
export const getPathToDesktopIndex = (pkg: 'desktop-gui' | 'launchpad') => {
let distDir = 'dist'
export const getPathToDesktopIndex = (pkg: 'desktop-gui' | 'launchpad', graphqlPort?: number) => {
// For now, if we see that there's a CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT
// we assume we're running Cypress targeting that (dev server)
if (pkg === 'launchpad' && process.env.CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT) {
return `http://localhost:${process.env.CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT}`
return `http://localhost:${process.env.CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT}?gqlPort=${graphqlPort}`
}
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF) {
distDir = 'dist-e2e'
}
return `file://${path.join(__dirname, '..', '..', pkg, distDir, 'index.html')}`
return `file://${path.join(__dirname, '..', '..', pkg, 'dist', 'index.html')}?gqlPort=${graphqlPort}`
}

View File

@@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"build-prod": "tsc --project .",
"clean": "rimraf lib/*.js || echo 'cleaned'",
"clean": "rimraf 'lib/**/*.js'",
"clean-deps": "rimraf node_modules",
"test": "yarn test-unit",
"test-debug": "yarn test-unit --inspect-brk=5566",

View File

@@ -21,7 +21,7 @@ const _debugOpts = !debug.enabled ? _.noop : (opts: RewriteOpts) => {
// because it does not require importing @packages/ts like development does.
// this has a huge performance impact, bringing the `responsiveMs` for threads
// from ~1s to about ~300ms on my system
const WORKER_FILENAME = process.env.CYPRESS_INTERNAL_ENV === 'production' ? 'worker.js' : 'worker-shim.js'
const WORKER_FILENAME = process.env.CYPRESS_INTERNAL_ENV === 'production' ? 'worker.js' : '../../script/worker-shim.js'
const WORKER_PATH = path.join(__dirname, WORKER_FILENAME)

View File

@@ -6,6 +6,7 @@
"scripts": {
"build-prod": "tsc --project .",
"build-test": "yarn build-prod --noEmit",
"clean": "rimraf 'lib/**/*.js'",
"clean-deps": "rimraf node_modules",
"test": "mocha --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json"
},

View File

@@ -1,6 +1,7 @@
// Moved outside of /lib so we can rm -rf "lib/**/*.js" without deleting this
if (process.env.CYPRESS_INTERNAL_ENV === 'production') {
throw new Error(`${__filename} should only run outside of prod`)
}
require('@packages/ts/register')
require('./worker.ts')
require('../lib/threads/worker.ts')

View File

@@ -57,7 +57,7 @@
"sockjs-client": "^1.5.0",
"strip-ansi": "6.0.0",
"ts-loader": "^8.0.5",
"ts-node": "8.3.0",
"ts-node": "^10.2.1",
"watch": "^1.0.2",
"webpack": "4.44.2"
},

View File

@@ -3,7 +3,7 @@ import { EventEmitter } from 'events'
import Promise from 'bluebird'
import { action } from 'mobx'
import { client } from '@packages/socket'
import { client } from '@packages/socket/lib/browser'
import type { BaseStore } from './store'
import { studioRecorder } from './studio'
@@ -14,11 +14,15 @@ import { selectorPlaygroundModel } from './selector-playground'
import $Cypress from '@packages/driver'
import type { automationElementId } from './automation-element'
const $ = $Cypress.$
const ws = client.connect({
const PORT_MATCH = /serverPort=(\d+)/.exec(window.location.search)
const socketConfig = {
path: '/__socket.io',
transports: ['websocket'],
})
}
const $ = $Cypress.$
const ws = PORT_MATCH ? client(`http://localhost:${PORT_MATCH[1]}`, socketConfig) : client(socketConfig)
ws.on('connect', () => {
ws.emit('runner:connected')

View File

@@ -19,9 +19,11 @@ register({
},
})
const io = returnMockRequire('@packages/socket/lib/browser', { client: {} })
io.client.connect = sinon.stub().returns({ emit: () => {}, on: () => {} })
returnMockRequire('@packages/socket/lib/browser', {
client () {
return { emit: () => {}, on: () => {} }
},
})
const _useFakeTimers = sinon.useFakeTimers
let timers = []

View File

@@ -1,5 +1,5 @@
{
"extends": "../ts/tsconfig.json",
"extends": "../ts/tsconfig.dom.json",
"compilerOptions": {
"jsx": "react",
"experimentalDecorators": true

View File

@@ -19,9 +19,11 @@ register({
},
})
const io = returnMockRequire('@packages/socket/lib/browser', { client: {} })
io.client.connect = sinon.stub().returns({ emit: () => {}, on: () => {} })
returnMockRequire('@packages/socket/lib/browser', {
client () {
return { emit: () => {}, on: () => {} }
},
})
const _useFakeTimers = sinon.useFakeTimers
let timers = []

View File

@@ -11,7 +11,6 @@ const errors = require('../errors')
const machineId = require('../util/machine_id')
const random = require('../util/random')
const user = require('../user')
const windows = require('./windows')
let app
let authCallback
@@ -101,7 +100,7 @@ const start = (onMessage, utmCode) => {
})
.finally(() => {
_stopServer()
windows.focusMainWindow()
require('./windows').focusMainWindow()
})
}

View File

@@ -6,6 +6,7 @@ const { clipboard } = require('electron')
const debug = require('debug')('cypress:server:events')
const pluralize = require('pluralize')
const stripAnsi = require('strip-ansi')
const dialog = require('./dialog')
const pkg = require('./package')
const logs = require('./logs')
@@ -26,21 +27,11 @@ const fileOpener = require('../util/file-opener')
const api = require('../api')
const savedState = require('../saved_state')
import * as config from '../config'
import auth from './auth'
import user from '../user'
import { openProject } from '../open_project'
import specsUtil from '../util/specs'
import { setDataContext, startGraphQLServer } from '@packages/graphql/src/server'
import { getProjectRoots, insertProject, removeLatestProjects, removeProject } from '@packages/server/lib/cache'
import { checkAuthQuery } from '@packages/graphql/src/stitching/remoteGraphQLCalls'
import type { FindSpecs, FoundBrowser, LaunchArgs, LaunchOpts, OpenProjectLaunchOptions } from '@packages/types'
import type { LaunchArgs } from '@packages/types'
import type { EventEmitter } from 'events'
import { makeDataContext } from '@packages/data-context'
import browserUtils from '../browsers/utils'
const { getBrowsers } = browserUtils
const nullifyUnserializableValues = (obj) => {
// nullify values that cannot be cloned
@@ -167,7 +158,7 @@ const handleEvent = function (options, bus, event, id, type, arg) {
})
return openProject.launch(arg.browser, fullSpec, {
// TODO: Tim see why this
// TODO: Tim see why this "projectRoot" is passed along
projectRoot: options.projectRoot,
onBrowserOpen () {
return send({ browserOpened: true })
@@ -495,7 +486,11 @@ const handleEvent = function (options, bus, event, id, type, arg) {
}
}
module.exports = {
interface EventsStartArgs extends LaunchArgs {
onFocusTests: () => void
}
export = {
nullifyUnserializableValues,
handleEvent,
@@ -504,72 +499,8 @@ module.exports = {
return ipc.removeAllListeners()
},
async start (options: LaunchArgs, bus: EventEmitter, { startGraphQL } = { startGraphQL: true }) {
async start (options: EventsStartArgs, bus: EventEmitter) {
// curry left options
ipc.on('request', _.partial(this.handleEvent, options, bus))
// support not starting server for testing purposes.
if (!startGraphQL) {
return
}
// TODO: Figure out how we want to cleanup & juggle the config, so it's not jammed
// into the projects
startGraphQLServer()
const ctx = await makeDataContext({
launchArgs: options,
launchOptions: {},
appApi: {
getBrowsers () {
return getBrowsers()
},
},
authApi: {
logIn () {
return auth.start(() => {}, 'launchpad')
},
logOut () {
return user.logOut()
},
checkAuth (context) {
return checkAuthQuery(context)
},
},
projectApi: {
getConfig (projectRoot: string) {
return config.get(projectRoot)
},
launchProject (browser: FoundBrowser, spec: Cypress.Spec, options?: LaunchOpts) {
return openProject.launch({ ...browser }, spec, options)
},
initializeProject (args: LaunchArgs, options: OpenProjectLaunchOptions, browsers: FoundBrowser[]) {
return openProject.create(args.projectRoot, args, options, browsers)
},
insertProjectToCache (projectRoot: string) {
insertProject(projectRoot)
},
removeProjectFromCache (projectRoot: string) {
removeProject(projectRoot)
},
getProjectRootsFromCache () {
return getProjectRoots()
},
findSpecs (payload: FindSpecs) {
return specsUtil.findSpecs(payload)
},
clearLatestProjectsCache () {
return removeLatestProjects()
},
},
})
// Fetch the browsers when the app starts, so we have some by
// the time we're continuing.
ctx.actions.app.refreshBrowsers()
// load projects from cache on start
ctx.actions.project.loadProjects()
setDataContext(ctx)
},
}

View File

@@ -1,5 +1,6 @@
import _ from 'lodash'
import { shell } from 'electron'
import { URL, URLSearchParams } from 'url'
// NOTE: in order for query params to be passed through on links
// forwardQueryParams: true must be set for that slug in the on package

View File

@@ -0,0 +1,64 @@
import express from 'express'
import { addGraphQLHTTP } from '@packages/graphql/src/server'
import type { AddressInfo } from 'net'
import type { DataContext } from '@packages/data-context'
import pDefer from 'p-defer'
import cors from 'cors'
import type { Server } from 'http'
import { SocketIOServer } from '@packages/socket'
let graphqlServer: Server | undefined
export async function closeGraphQLServer () {
if (!graphqlServer) {
return
}
const dfd = pDefer()
graphqlServer.close((err) => {
if (err) {
dfd.reject()
}
dfd.resolve()
})
graphqlServer = undefined
dfd.promise
}
export async function makeGraphQLServer (ctx: DataContext) {
const dfd = pDefer<number>()
const app = express()
app.use(cors())
// TODO: Figure out how we want to cleanup & juggle the config, so
// it's not jammed into the projects
addGraphQLHTTP(app, ctx)
const srv = graphqlServer = app.listen(() => {
const port = (srv.address() as AddressInfo).port
const endpoint = `http://localhost:${port}/graphql`
if (process.env.NODE_ENV === 'development') {
/* eslint-disable-next-line no-console */
console.log(`GraphQL server is running at ${endpoint}`)
}
ctx.debug(`GraphQL Server at ${endpoint}`)
dfd.resolve(port)
})
const socketServer = new SocketIOServer(srv, {
path: '/__gqlSocket',
transports: ['websocket'],
})
ctx.emitter.setLaunchpadSocketServer(socketServer)
return dfd.promise
}

View File

@@ -13,19 +13,20 @@ export type WindowOptions = Electron.BrowserWindowConstructorOptions & {
type?: 'INDEX'
url?: string
devTools?: boolean
graphqlPort?: number
}
let windows = {}
let recentlyCreatedWindow = false
const getUrl = function (type) {
const getUrl = function (type, port?: number) {
switch (type) {
case 'INDEX':
if (process.env.LAUNCHPAD) {
return getPathToDesktopIndex('launchpad')
return getPathToDesktopIndex('launchpad', port)
}
return getPathToDesktopIndex('desktop-gui')
return getPathToDesktopIndex('desktop-gui', port)
default:
throw new Error(`No acceptable window type found for: '${type}'`)
@@ -158,14 +159,6 @@ export function create (projectRoot, _options: WindowOptions = {}, newBrowserWin
options.webPreferences.partition = options.partition
}
// When we're E2E testing the launchpad or app, we want to stand up a real Cy server.
// It's best to do this without rendering the launchpad, so we won't visually render the electron window.
// TODO(jess): Is it better to stub the electron window? The server is pretty coupled to it.
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF) {
options.frame = false
options.show = false
}
const win = newBrowserWindow(options)
win.on('blur', function (...args) {
@@ -223,7 +216,7 @@ export function create (projectRoot, _options: WindowOptions = {}, newBrowserWin
}
// open desktop-gui BrowserWindow
export function open (projectRoot, options: WindowOptions = {}, newBrowserWindow = _newBrowserWindow) {
export function open (projectRoot, graphqlPort: number | undefined, options: WindowOptions = {}, newBrowserWindow = _newBrowserWindow): Bluebird<BrowserWindow> {
// if we already have a window open based
// on that type then just show + focus it!
let win
@@ -249,7 +242,7 @@ export function open (projectRoot, options: WindowOptions = {}, newBrowserWindow
})
if (!options.url) {
options.url = getUrl(options.type)
options.url = getUrl(options.type, graphqlPort)
}
win = create(projectRoot, options, newBrowserWindow)

View File

@@ -0,0 +1,71 @@
import { DataContext } from '@packages/data-context'
import specsUtil from './util/specs'
import type { FindSpecs, FoundBrowser, LaunchArgs, LaunchOpts, OpenProjectLaunchOptions, PlatformName } from '@packages/types'
import { checkAuthQuery } from '@packages/graphql/src/stitching/remoteGraphQLCalls'
import browserUtils from './browsers/utils'
import auth from './gui/auth'
import user from './user'
import * as config from './config'
import type { EventEmitter } from 'events'
import { openProject } from './open_project'
import cache from './cache'
const { getBrowsers } = browserUtils
interface MakeDataContextOptions {
os: PlatformName
rootBus: EventEmitter
launchArgs: LaunchArgs
}
export function makeDataContext (options: MakeDataContextOptions) {
return new DataContext({
...options,
launchOptions: {},
appApi: {
getBrowsers () {
return getBrowsers()
},
},
authApi: {
logIn () {
return auth.start(() => {}, 'launchpad')
},
logOut () {
return user.logOut()
},
checkAuth (context) {
return checkAuthQuery(context)
},
},
projectApi: {
getConfig (projectRoot: string) {
return config.get(projectRoot)
},
launchProject (browser: FoundBrowser, spec: Cypress.Spec, options?: LaunchOpts) {
return openProject.launch({ ...browser }, spec, options)
},
initializeProject (args: LaunchArgs, options: OpenProjectLaunchOptions, browsers: FoundBrowser[]) {
return openProject.create(args.projectRoot, args, options, browsers)
},
insertProjectToCache (projectRoot: string) {
cache.insertProject(projectRoot)
},
getProjectRootsFromCache () {
return cache.getProjectRoots()
},
findSpecs (payload: FindSpecs) {
return specsUtil.findSpecs(payload)
},
clearLatestProjectsCache () {
return cache.removeLatestProjects()
},
removeProjectFromCache (path: string) {
return cache.removeProject(path)
},
closeActiveProject () {
return openProject.closeOpenProjectAndBrowsers()
},
},
})
}

View File

@@ -1,4 +1,4 @@
module.exports = (mode, options) => {
export = (mode, options) => {
if (mode === 'record') {
return require('./record').run(options)
}

View File

@@ -1,19 +1,22 @@
const _ = require('lodash')
const os = require('os')
const EE = require('events')
const { app } = require('electron')
const image = require('electron').nativeImage
const cyIcons = require('@cypress/icons')
const savedState = require('../saved_state')
const menu = require('../gui/menu')
const Events = require('../gui/events')
const Windows = require('../gui/windows')
import _ from 'lodash'
import os from 'os'
import { app, nativeImage as image } from 'electron'
// eslint-disable-next-line no-duplicate-imports
import type { WebContents } from 'electron'
import cyIcons from '@cypress/icons'
import savedState from '../saved_state'
import menu from '../gui/menu'
import Events from '../gui/events'
import * as Windows from '../gui/windows'
import { runInternalServer } from './internal-server'
import type { LaunchArgs, PlatformName } from '@packages/types'
import EventEmitter from 'events'
const isDev = () => {
return process.env['CYPRESS_INTERNAL_ENV'] === 'development'
}
module.exports = {
export = {
isMac () {
return os.platform() === 'darwin'
},
@@ -80,7 +83,7 @@ module.exports = {
y: 'appY',
devTools: 'isAppDevToolsOpen',
},
onBlur () {
onBlur (this: {webContents: WebContents}) {
if (this.webContents.isDevToolsOpened()) {
return
}
@@ -121,10 +124,15 @@ module.exports = {
return args[os.platform()]
},
ready (options = {}) {
const bus = new EE
/**
* @param {import('@packages/types').LaunchArgs} options
* @returns
*/
ready (options: {projectRoot?: string} = {}) {
const { projectRoot } = options
const { serverPortPromise, bus } = process.env.LAUNCHPAD
? runInternalServer(options)
: { bus: new EventEmitter, serverPortPromise: Promise.resolve(undefined) }
// TODO: potentially just pass an event emitter
// instance here instead of callback functions
@@ -135,19 +143,21 @@ module.exports = {
},
})
return savedState.create(projectRoot, false)
.then((state) => {
return state.get()
})
.then((state) => {
return Windows.open(projectRoot, this.getWindowArgs(state, options))
return Promise.all([
serverPortPromise,
savedState.create(projectRoot, false).then((state) => state.get()),
])
.then(([port, state]) => {
return Windows.open(projectRoot, port, this.getWindowArgs(state))
.then((win) => {
Events.start(_.extend({}, options, {
Events.start({
...(options as LaunchArgs),
onFocusTests () {
// @ts-ignore
return app.focus({ steal: true }) || win.focus()
},
os: os.platform(),
}), bus)
os: os.platform() as PlatformName,
}, bus)
return win
})

View File

@@ -0,0 +1,31 @@
import os from 'os'
import { EventEmitter } from 'events'
import { makeDataContext } from '../makeDataContext'
import { makeGraphQLServer } from '../gui/makeGraphQLServer'
import { assertValidPlatform } from '@packages/types/src/platform'
export function runInternalServer (options) {
const bus = new EventEmitter()
const platform = os.platform()
assertValidPlatform(platform)
const ctx = makeDataContext({
os: platform,
rootBus: bus,
launchArgs: options,
})
// Initializing the data context, loading browsers, etc.
ctx.initializeData()
ctx.emitter.init()
const serverPortPromise = makeGraphQLServer(ctx)
serverPortPromise.then((port) => {
ctx.setGqlServerPort(port)
})
return { ctx, bus, serverPortPromise }
}

View File

@@ -13,10 +13,10 @@ import * as session from './session'
import { getSpecUrl } from './project_utils'
import errors from './errors'
import type { LaunchOpts, LaunchArgs, OpenProjectLaunchOptions, FoundBrowser } from '@packages/types'
import { closeGraphQLServer } from '@packages/graphql/src/server'
import { fs } from './util/fs'
import path from 'path'
import os from 'os'
import { closeGraphQLServer } from './gui/makeGraphQLServer'
const debug = Debug('cypress:server:open_project')
@@ -229,6 +229,10 @@ export class OpenProject {
session.clearSessions()
})
.then(() => {
if (options.skipBrowserOpenForTest) {
return
}
return browsers.open(browser, options, automation)
})
}
@@ -380,8 +384,6 @@ export class OpenProject {
this.componentSpecsWatcher.close()
this.componentSpecsWatcher = null
}
return closeGraphQLServer()
}
closeBrowser () {
@@ -405,7 +407,10 @@ export class OpenProject {
this.stopSpecsWatcher()
return this.closeOpenProjectAndBrowsers()
return Promise.all([
closeGraphQLServer(),
this.closeOpenProjectAndBrowsers(),
]).then(() => null)
}
async create (path: string, args: LaunchArgs, options: OpenProjectLaunchOptions, browsers: FoundBrowser[] = []) {

View File

@@ -98,9 +98,11 @@ const init = (config, options) => {
debug('forking to run %s', childIndexFilename)
if (inspector.url()) {
const inspectType = process.argv.some((a) => a.startsWith('--inspect-brk')) ? '--inspect-brk' : '--inspect'
childOptions.execArgv = _.chain(process.execArgv.slice(0))
.remove('--inspect-brk')
.push(`--inspect=${process.debugPort + 1}`)
.push(`${inspectType}=${process.debugPort + 1}`)
.value()
}

View File

@@ -33,6 +33,7 @@ import preprocessor from './plugins/preprocessor'
import { SpecsStore } from './specs-store'
import { checkSupportFile, getDefaultConfigFilePath } from './project_utils'
import type { FoundBrowser, OpenProjectLaunchOptions } from '@packages/types'
import { DataContextShell } from '@packages/data-context/src/DataContextShell'
// Cannot just use RuntimeConfigOptions as is because some types are not complete.
// Instead, this is an interface of values that have been manually validated to exist
@@ -65,6 +66,7 @@ export class ProjectBase<TServer extends Server> extends EE {
public id: string
protected watchers: Watchers
protected ctx: DataContextShell
protected _cfg?: Cfg
protected _server?: TServer
protected _automation?: Automation
@@ -82,7 +84,7 @@ export class ProjectBase<TServer extends Server> extends EE {
constructor ({
projectRoot,
testingType,
options,
options = {},
}: {
projectRoot: string
testingType: Cypress.TestingType
@@ -104,6 +106,7 @@ export class ProjectBase<TServer extends Server> extends EE {
this.spec = null
this.browser = null
this.id = createHmac('sha256', 'secret-key').update(projectRoot).digest('hex')
this.ctx = options.ctx ?? new DataContextShell()
debug('Project created %o', {
testingType: this.testingType,
@@ -162,8 +165,8 @@ export class ProjectBase<TServer extends Server> extends EE {
createServer (testingType: Cypress.TestingType) {
return testingType === 'e2e'
? new ServerE2E() as TServer
: new ServerCt() as TServer
? new ServerE2E(this.ctx) as TServer
: new ServerCt(this.ctx) as TServer
}
async open () {
@@ -186,7 +189,9 @@ export class ProjectBase<TServer extends Server> extends EE {
this._server = this.createServer(this.testingType)
cfg = await this.initializePlugins(cfg, this.options)
if (!this.options.skipPluginIntializeForTesting) {
cfg = await this.initializePlugins(cfg, this.options)
}
const {
specsStore,
@@ -209,6 +214,7 @@ export class ProjectBase<TServer extends Server> extends EE {
specsStore,
})
this.ctx.setAppServerPort(port)
this._isServerOpen = true
// if we didnt have a cfg.port
@@ -337,7 +343,10 @@ export class ProjectBase<TServer extends Server> extends EE {
return
}
const closePreprocessor = (this.testingType === 'e2e' && preprocessor.close) ?? undefined
const closePreprocessor = this.testingType === 'e2e' ? preprocessor.close : undefined
this.ctx.setAppServerPort(undefined)
this.ctx.emitter.setAppSocketServer(undefined)
await Promise.all([
this.server?.close(),
@@ -436,7 +445,7 @@ export class ProjectBase<TServer extends Server> extends EE {
config,
}: {
specs: Cypress.Cypress['spec'][]
config: any
config: Cfg
}) {
const specsStore = new SpecsStore(config, this.testingType)
@@ -458,7 +467,7 @@ export class ProjectBase<TServer extends Server> extends EE {
let ctDevServerPort: number | undefined
if (this.testingType === 'component') {
if (this.testingType === 'component' && !this.options.skipPluginIntializeForTesting) {
const { port } = await this.startCtDevServer(specs, config)
ctDevServerPort = port
@@ -595,7 +604,7 @@ export class ProjectBase<TServer extends Server> extends EE {
this._automation = new Automation(namespace, socketIoCookie, screenshotsFolder, onBrowserPreRequest, onRequestEvent)
this.server.startWebsockets(this.automation, this.cfg, {
const io = this.server.startWebsockets(this.automation, this.cfg, {
onReloadBrowser: options.onReloadBrowser,
onFocusTests: options.onFocusTests,
onSpecChanged: options.onSpecChanged,
@@ -651,6 +660,8 @@ export class ProjectBase<TServer extends Server> extends EE {
return
},
})
this.ctx.emitter.setAppSocketServer(io)
}
changeToUrl (url) {

View File

@@ -5,6 +5,8 @@ import { Request, Response, Router } from 'express'
import send from 'send'
import { getPathToDist } from '@packages/resolve-dist'
import type { InitializeRoutes } from './routes'
import { fs } from './util/fs'
import type { DataContextShell } from '@packages/data-context/src/DataContextShell'
const debug = Debug('cypress:server:routes-ct')
@@ -15,6 +17,7 @@ const serveChunk = (req: Request, res: Response, clientRoute: string) => {
}
export const createRoutesCT = ({
ctx,
config,
nodeProxy,
getCurrentBrowser,
@@ -23,20 +26,52 @@ export const createRoutesCT = ({
const routesCt = Router()
if (process.env.CYPRESS_INTERNAL_VITE_APP_PORT) {
const myProxy = httpProxy.createProxyServer({
const proxy = httpProxy.createProxyServer({
target: `http://localhost:${process.env.CYPRESS_INTERNAL_VITE_APP_PORT}/`,
})
const proxyIndex = httpProxy.createProxyServer({
target: `http://localhost:${process.env.CYPRESS_INTERNAL_VITE_APP_PORT}/`,
selfHandleResponse: true,
})
proxyIndex.on('proxyRes', function (proxyRes, _req, _res) {
const body: any[] = []
proxyRes.on('data', function (chunk) {
let chunkData = String(chunk)
if (chunkData.includes('<head>')) {
chunkData = chunkData.replace('<body>', replaceBody(ctx))
}
body.push(chunkData)
})
proxyRes.on('end', function () {
(_res as Response).send(body.join(''))
})
})
// TODO: can namespace this onto a "unified" route like __app-unified__
// make sure to update the generated routes inside of vite.config.ts
routesCt.get('/__vite__/*', (req, res) => {
myProxy.web(req, res, {}, (e) => {
})
if (req.params[0] === '') {
proxyIndex.web(req, res, {}, (e) => {})
} else {
proxy.web(req, res, {}, (e) => {})
}
})
} else {
routesCt.get('/__vite__/*', (req, res) => {
const pathToFile = getPathToDist('app', req.params[0])
if (req.params[0] === '') {
return fs.readFile(pathToFile, 'utf8')
.then((file) => {
res.send(file.replace('<body>', replaceBody(ctx)))
})
}
return send(req, pathToFile).pipe(res)
})
}
@@ -122,3 +157,7 @@ export const createRoutesCT = ({
return routesCt
}
function replaceBody (ctx: DataContextShell) {
return `<body><script>window.__CYPRESS_GRAPHQL_PORT__ = ${JSON.stringify(ctx.gqlServerPort)};</script>\n`
}

View File

@@ -9,10 +9,12 @@ import type { Cfg } from './project-base'
import xhrs from './controllers/xhrs'
import { runner } from './controllers/runner'
import { iframesController } from './controllers/iframes'
import type { DataContextShell } from '@packages/data-context/src/DataContextShell'
const debug = Debug('cypress:server:routes')
export interface InitializeRoutes {
ctx: DataContextShell
specsStore: SpecsStore
config: Cfg
getSpec: () => Cypress.Spec | null

View File

@@ -31,6 +31,7 @@ import type { Browser } from '@packages/server/lib/browsers/types'
import { InitializeRoutes, createCommonRoutes } from './routes'
import { createRoutesE2E } from './routes-e2e'
import { createRoutesCT } from './routes-ct'
import type { DataContextShell } from '@packages/data-context/src/DataContextShell'
const ALLOWED_PROXY_BYPASS_URLS = [
'/',
@@ -126,7 +127,7 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
protected _remoteDomainName: unknown
protected _remoteFileServer: unknown
constructor () {
constructor (private ctx: DataContextShell) {
this.isListening = false
// @ts-ignore
this.request = Request()
@@ -211,6 +212,7 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
this.createHosts(config.hosts)
const routeOptions: InitializeRoutes = {
ctx: this.ctx,
config,
specsStore,
getRemoteState,
@@ -621,9 +623,4 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
sendSpecList (specs: Cypress.Cypress['spec'][], testingType: Cypress.TestingType) {
return this.socket.sendSpecList(specs, testingType)
}
// used for testing
__setMiddleware (middleware: any) {
this._middleware = middleware
}
}

View File

@@ -3,6 +3,7 @@ import Debug from 'debug'
import isHtml from 'is-html'
import _ from 'lodash'
import stream from 'stream'
import { EventEmitter } from 'events'
import url from 'url'
import httpsProxy from '@packages/https-proxy'
import { getRouteForRequest } from '@packages/net-stubbing'
@@ -16,6 +17,7 @@ import * as ensureUrl from './util/ensure-url'
import headersUtil from './util/headers'
import statusCode from './util/status_code'
import type { Cfg } from './project-base'
import { DataContextShell } from '@packages/data-context/src/DataContextShell'
type WarningErr = Record<string, any>
@@ -44,8 +46,8 @@ const isResponseHtml = function (contentType, responseBuffer) {
export class ServerE2E extends ServerBase<SocketE2E> {
private _urlResolver: Bluebird<Record<string, any>> | null
constructor () {
super()
constructor (ctx: DataContextShell = new DataContextShell({ rootBus: new EventEmitter })) {
super(ctx)
this._urlResolver = null
}

View File

@@ -21,15 +21,15 @@ export class SpecsStore {
constructor (
private cypressConfig: Record<string, any>,
private runner: Cypress.TestingType,
private testingType: Cypress.TestingType,
) {}
get specDirectory () {
if (this.runner === 'e2e') {
if (this.testingType === 'e2e') {
return this.cypressConfig.resolved.integrationFolder.value
}
if (this.runner === 'component') {
if (this.testingType === 'component') {
return this.cypressConfig.resolved.componentFolder.value
}
}

View File

@@ -1,11 +1,17 @@
const debug = require('debug')('cypress:server:ts-node')
// @ts-check
const debugLib = require('debug')
const path = require('path')
const tsnode = require('ts-node')
const resolve = require('./resolve')
const debug = debugLib('cypress:server:ts-node')
const getTsNodeOptions = (tsPath, registeredFile) => {
return {
compiler: tsPath, // use the user's installed typescript
/**
* @type {import('ts-node').RegisterOptions}
*/
const opts = {
compiler: process.env.TS_NODE_COMPILER || tsPath, // use the user's installed typescript
compilerOptions: {
module: 'CommonJS',
},
@@ -14,6 +20,8 @@ const getTsNodeOptions = (tsPath, registeredFile) => {
dir: path.dirname(registeredFile),
transpileOnly: true, // transpile only (no type-check) for speed
}
return opts
}
const register = (projectRoot, registeredFile) => {

View File

@@ -2,6 +2,8 @@ const _ = require('lodash')
const debug = require('debug')('cypress:server:validation')
const is = require('check-more-types')
const { commaListsOr } = require('common-tags')
const { URL } = require('url')
const configOptions = require('../config_options')
const path = require('path')

View File

@@ -49,6 +49,7 @@
"compression": "1.7.4",
"content-type": "1.0.4",
"cookie-parser": "1.4.5",
"cors": "2.8.5",
"data-uri-to-buffer": "2.0.1",
"dayjs": "^1.9.3",
"debug": "4.3.2",
@@ -65,6 +66,7 @@
"fluent-ffmpeg": "2.1.2",
"fs-extra": "8.1.0",
"get-port": "5.1.1",
"getenv": "1.0.0",
"getos": "3.2.1",
"glob": "7.1.3",
"graceful-fs": "4.2.0",
@@ -94,6 +96,7 @@
"node-machine-id": "1.1.12",
"opn": "cypress-io/opn#2f4e9a216ca7bdb95dfae9d46d99ddf004b3cbb5",
"ospath": "1.2.2",
"p-defer": "^3.0.0",
"p-queue": "6.1.0",
"pluralize": "8.0.0",
"ramda": "0.27.1",
@@ -115,7 +118,7 @@
"tough-cookie": "4.0.0",
"trash": "5.2.0",
"tree-kill": "1.2.2",
"ts-node": "8.5.4",
"ts-node": "^10.2.1",
"tslib": "2.3.0",
"underscore.string": "3.3.5",
"url-parse": "1.5.2",
@@ -131,10 +134,12 @@
"@cypress/json-schemas": "5.39.0",
"@cypress/sinon-chai": "2.9.1",
"@ffprobe-installer/ffprobe": "1.1.0",
"@packages/data-context": "0.0.0-development",
"@packages/desktop-gui": "0.0.0-development",
"@packages/electron": "0.0.0-development",
"@packages/example": "0.0.0-development",
"@packages/extension": "0.0.0-development",
"@packages/graphql": "0.0.0-development",
"@packages/https-proxy": "0.0.0-development",
"@packages/launcher": "0.0.0-development",
"@packages/net-stubbing": "0.0.0-development",
@@ -156,7 +161,6 @@
"chai-uuid": "1.0.6",
"chokidar-cli": "2.1.0",
"chrome-har-capturer": "0.13.4",
"cors": "2.8.5",
"cross-env": "6.0.3",
"devtools-protocol": "0.0.839267",
"eol": "0.9.1",

View File

@@ -5,7 +5,7 @@ const ws = require('ws')
const httpsProxyAgent = require('https-proxy-agent')
const evilDns = require('evil-dns')
const Promise = require('bluebird')
const socketIo = require(`${root}../socket`)
const socketIo = require(`${root}../socket/lib/browser`)
const httpsServer = require(`${root}../https-proxy/test/helpers/https_server`)
const config = require(`${root}lib/config`)
const { ServerE2E } = require(`${root}lib/server-e2e`)

View File

@@ -1,7 +1,7 @@
const { expect } = require('chai')
const HttpsProxyAgent = require('https-proxy-agent')
const os = require('os')
const socketIo = require('@packages/socket')
const socketIo = require('@packages/socket/lib/browser')
module.exports = (on) => {
on('task', {

View File

@@ -79,7 +79,7 @@ describe('lib/gui/events', () => {
it('ipc attaches callback on request', () => {
sinon.stub(events, 'handleEvent')
events.start({ foo: 'bar' }, {}, { startGraphQL: false })
events.start({ foo: 'bar' }, {})
expect(electron.ipcMain.on).to.be.calledWith('request')
})
@@ -88,7 +88,7 @@ describe('lib/gui/events', () => {
electron.ipcMain.on.yields('arg1', 'arg2')
const handleEvent = sinon.stub(events, 'handleEvent')
events.start({ foo: 'bar' }, {}, { startGraphQL: false })
events.start({ foo: 'bar' }, {})
expect(handleEvent).to.be.calledWith({ foo: 'bar' }, {}, 'arg1', 'arg2')
})

View File

@@ -47,7 +47,7 @@ describe('lib/gui/windows', () => {
type: 'INDEX',
}
return Windows.open('/path/to/project', options, () => this.win)
return Windows.open('/path/to/project', undefined, options, () => this.win)
.then((win) => {
expect(options).to.include({
height: 500,

View File

@@ -3,7 +3,7 @@ require('../spec_helper')
const _ = require('lodash')
const path = require('path')
const Promise = require('bluebird')
const socketIo = require('@packages/socket')
const socketIo = require('@packages/socket/lib/browser')
const httpsAgent = require('https-proxy-agent')
const errors = require(`${root}lib/errors`)
const config = require(`${root}lib/config`)

View File

@@ -8,9 +8,7 @@
"./../ts/index.d.ts"
],
"compilerOptions": {
"lib": ["ES2020"],
"types": ["mocha", "node"],
"importHelpers": true,
"resolveJsonModule": true,
"noUnusedLocals": false,
}

View File

@@ -5,12 +5,11 @@ This is a shared lib for holding both the `socket.io` server and client.
## Using
```javascript
const socket = require("packages/socket")
const socket = require("@packages/socket")
// returns
{
server: require("socket.io"),
client: require("socket.io-client"),
getPathToClientSource: function () {
// returns path to the client 'socket.io.js' file
// for use in the browser
@@ -19,7 +18,7 @@ const socket = require("packages/socket")
```
```javascript
const socket = require("packages/socket")
const socket = require("@packages/socket")
// server usage
const srv = require("http").createServer()
@@ -27,6 +26,7 @@ const io = socket.server(srv)
io.on("connection", function(){})
// client usage
const { client } = require("@packages/socket/lib/client")
const client = socket.client("http://localhost:2020")
client.on("connect", function(){})
client.on("event", function(){})

View File

@@ -1,18 +1,5 @@
'use strict'
let __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
if (k2 === undefined) k2 = k
Object.defineProperty(o, k2, { enumerable: true, get () {
return m[k]
} })
}) : (function (o, m, k, k2) {
if (k2 === undefined) k2 = k
o[k2] = m[k]
}))
let __exportStar = (this && this.__exportStar) || function (m, exports) {
for (let p in m) if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p)
if (process.env.CYPRESS_INTERNAL_ENV !== 'production') {
require('@packages/ts/register')
}
Object.defineProperty(exports, '__esModule', { value: true })
__exportStar(require('./lib/socket'), exports)
module.exports = require('./lib/socket')

View File

@@ -1 +0,0 @@
export * from './lib/socket'

View File

@@ -1,11 +0,0 @@
'use strict'
let __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { 'default': mod }
}
Object.defineProperty(exports, '__esModule', { value: true })
exports.client = void 0
const socket_io_client_1 = __importDefault(require('socket.io-client'))
exports.client = socket_io_client_1.default

View File

@@ -1,5 +1,7 @@
import io from 'socket.io-client'
export type { Socket } from 'socket.io-client'
export {
io as client,
}

View File

@@ -1,83 +0,0 @@
'use strict'
let __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
if (k2 === undefined) k2 = k
Object.defineProperty(o, k2, { enumerable: true, get () {
return m[k]
} })
}) : (function (o, m, k, k2) {
if (k2 === undefined) k2 = k
o[k2] = m[k]
}))
let __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) {
Object.defineProperty(o, 'default', { enumerable: true, value: v })
}) : function (o, v) {
o['default'] = v
})
let __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod
let result = {}
if (mod != null) for (let k in mod) if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k)
__setModuleDefault(result, mod)
return result
}
let __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { 'default': mod }
}
Object.defineProperty(exports, '__esModule', { value: true })
exports.getClientSource = exports.getClientVersion = exports.getPathToClientSource = exports.SocketIOServer = exports.server = exports.client = void 0
const fs_1 = __importDefault(require('fs'))
const socket_io_1 = __importStar(require('socket.io'))
exports.server = socket_io_1.default
const browser_1 = require('./browser')
Object.defineProperty(exports, 'client', { enumerable: true, get () {
return browser_1.client
} })
const HUNDRED_MEGABYTES = 1e8 // 100000000
const { version } = require('socket.io-client/package.json')
const clientSource = require.resolve('socket.io-client/dist/socket.io.js')
class SocketIOServer extends socket_io_1.Server {
constructor (srv, opts) {
let _a
// in socket.io v3, they reduced down the max buffer size
// from 100mb to 1mb, so we reset it back to the previous value
//
// previous commit for reference:
// https://github.com/socketio/engine.io/blame/61b949259ed966ef6fc8bfd61f14d1a2ef06d319/lib/server.js#L29
opts = opts !== null && opts !== void 0 ? opts : {}
opts.maxHttpBufferSize = (_a = opts.maxHttpBufferSize) !== null && _a !== void 0 ? _a : HUNDRED_MEGABYTES
super(srv, opts)
}
}
exports.SocketIOServer = SocketIOServer
const getPathToClientSource = () => {
return clientSource
}
exports.getPathToClientSource = getPathToClientSource
const getClientVersion = () => {
return version
}
exports.getClientVersion = getClientVersion
const getClientSource = () => {
return fs_1.default.readFileSync(exports.getPathToClientSource(), 'utf8')
}
exports.getClientSource = getClientSource

View File

@@ -1,7 +1,6 @@
import fs from 'fs'
import type http from 'http'
import server, { Server as SocketIOBaseServer, ServerOptions, Socket } from 'socket.io'
import { client } from './browser'
export type { Socket }
@@ -30,7 +29,6 @@ class SocketIOServer extends SocketIOBaseServer {
}
export {
client,
server,
SocketIOServer,
}

View File

@@ -5,7 +5,8 @@
"main": "index.js",
"browser": "lib/browser.ts",
"scripts": {
"build-prod": "tsc || echo 'built, with type errors'",
"build-prod": "tsc || echo 'built, with type errors' && rm lib/browser.js",
"clean": "rimraf lib/*.js",
"check-ts": "tsc --noEmit",
"clean-deps": "rimraf node_modules",
"postinstall": "patch-package",
@@ -30,6 +31,7 @@
"lib",
"patches"
],
"types": "lib/socket.ts",
"workspaces": {
"nohoist": [
"socket.io",

Some files were not shown because too many files have changed in this diff Show More