diff --git a/circle.yml b/circle.yml index dd30f0363f..60b77e2f05 100644 --- a/circle.yml +++ b/circle.yml @@ -398,10 +398,16 @@ commands: description: ct or e2e type: enum enum: ['ct', 'e2e'] + debug: + description: debug option + type: string + default: '' steps: - restore_cached_workspace - run: command: | + DEBUG=<> \ + CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH='true' \ CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=$TEST_LAUNCHPAD_RECORD_KEY \ yarn workspace @packages/<> cypress:run:<> --browser <> --record --parallel --group <>-<> @@ -1184,12 +1190,13 @@ jobs: run-launchpad-component-tests-chrome: <<: *defaults - parallelism: 3 + parallelism: 7 steps: - run-new-ui-tests: browser: chrome package: launchpad type: ct + # debug: cypress:*,engine:socket run-launchpad-integration-tests-chrome: <<: *defaults diff --git a/package.json b/package.json index 39262fb718..bbf1aa5cd1 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@types/detect-port": "^1.3.1", "@types/enzyme-adapter-react-16": "1.0.5", "@types/execa": "0.9.0", + "@types/fluent-ffmpeg": "^2.1.18", "@types/fs-extra": "^8.0.1", "@types/getenv": "^1.0.0", "@types/glob": "7.1.1", diff --git a/packages/app/cypress/component/plugins/index.js b/packages/app/cypress/component/plugins/index.js index 248fccdcdf..1227b15a66 100644 --- a/packages/app/cypress/component/plugins/index.js +++ b/packages/app/cypress/component/plugins/index.js @@ -1,8 +1,3 @@ -/** - * @type {import('@cypress/vite-dev-server')} - */ -const { startDevServer } = require('@cypress/vite-dev-server') - /// // *********************************************************** // This example plugins/index.js can be used to load plugins @@ -20,23 +15,4 @@ const { startDevServer } = require('@cypress/vite-dev-server') * @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 - - if (config.testingType === 'component') { - on('dev-server:start', async (options) => { - return startDevServer({ - options, - viteConfig: { - // TODO(tim): Figure out why this isn't being picked up - optimizeDeps: { - include: ['@headlessui/vue'], - }, - }, - }) - }) - } - - return config // IMPORTANT to return a config -} +module.exports = require('@packages/frontend-shared/cypress/plugins/index') diff --git a/packages/data-context/src/DataContext.ts b/packages/data-context/src/DataContext.ts index eb9e398a74..277da21e19 100644 --- a/packages/data-context/src/DataContext.ts +++ b/packages/data-context/src/DataContext.ts @@ -1,17 +1,21 @@ import type { LaunchArgs, OpenProjectLaunchOptions } 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' import debugLib from 'debug' import fsExtra from 'fs-extra' -import type { AuthApiShape } from './actions/AuthActions' import { CoreDataShape, makeCoreData } from './data/coreDataShape' import { DataActions } from './DataActions' -import { AppDataSource } from './sources/AppDataSource' -import { ProjectDataSource } from './sources/ProjectDataSource' +import { + AppDataSource, + GitDataSource, + FileDataSource, + ProjectDataSource, + WizardDataSource, + BrowserDataSource, + UtilDataSource, +} from './sources/' import { cached } from './util/cached' -import { WizardDataSource } from './sources' -import type { AppApiShape, ProjectApiShape } from './actions' -import { makeLoaders } from './data/loaders' -import { BrowserDataSource } from './sources/BrowserDataSource' -import type { NexusGenAbstractTypeMembers } from '@packages/graphql/src/gen/nxs.gen' export interface DataContextConfig { launchArgs: LaunchArgs @@ -37,8 +41,6 @@ export class DataContext { this._coreData = config.coreData ?? makeCoreData() } - loaders = makeLoaders(this) - get launchArgs () { return this.config.launchArgs } @@ -59,6 +61,21 @@ export class DataContext { return this.coreData.app.browsers } + @cached + get util () { + return new UtilDataSource(this) + } + + @cached + get file () { + return new FileDataSource(this) + } + + @cached + get git () { + return new GitDataSource(this) + } + @cached get browser () { return new BrowserDataSource(this) @@ -138,6 +155,10 @@ export class DataContext { } dispose () { - this.loaders.dispose() + this.util.disposeLoaders() + } + + get loader () { + return this.util.loader } } diff --git a/packages/data-context/src/actions/FileActions.ts b/packages/data-context/src/actions/FileActions.ts new file mode 100644 index 0000000000..acc570c1e9 --- /dev/null +++ b/packages/data-context/src/actions/FileActions.ts @@ -0,0 +1,5 @@ +import type { DataContext } from '..' + +export class FileActions { + constructor (private ctx: DataContext) {} +} diff --git a/packages/data-context/src/actions/ProjectActions.ts b/packages/data-context/src/actions/ProjectActions.ts index bdfa4ade2f..5843d91cf6 100644 --- a/packages/data-context/src/actions/ProjectActions.ts +++ b/packages/data-context/src/actions/ProjectActions.ts @@ -55,8 +55,8 @@ export class ProjectActions { return this } - async findSpecs (projectRoot: string, specType: Maybe): Promise { - const config = await this.ctx.loaders.projectConfig(projectRoot) + async findSpecs (projectRoot: string, specType: Maybe) { + const config = await this.ctx.project.getConfig(projectRoot) const specs = await this.api.findSpecs({ projectRoot, fixturesFolder: config.fixturesFolder ?? false, diff --git a/packages/data-context/src/actions/index.ts b/packages/data-context/src/actions/index.ts index fdbdf84f92..29c097aabe 100644 --- a/packages/data-context/src/actions/index.ts +++ b/packages/data-context/src/actions/index.ts @@ -3,5 +3,6 @@ export * from './AppActions' export * from './AuthActions' +export * from './FileActions' export * from './ProjectActions' export * from './WizardActions' diff --git a/packages/data-context/src/data/index.ts b/packages/data-context/src/data/index.ts index e78993881f..048efd22e5 100644 --- a/packages/data-context/src/data/index.ts +++ b/packages/data-context/src/data/index.ts @@ -3,5 +3,3 @@ export * from './DataEmitter' export * from './coreDataShape' -export * from './loaders' -export * from './util/' diff --git a/packages/data-context/src/data/loaders.ts b/packages/data-context/src/data/loaders.ts deleted file mode 100644 index de81522d51..0000000000 --- a/packages/data-context/src/data/loaders.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { FullConfig } from '@packages/types' -import DataLoader from 'dataloader' -import fs from 'fs-extra' -import type { DataContext } from '..' -import { GitInfo, getGitInfo } from './util' - -/** - * Centralized location to load files. Allows us to consolidate - * file watching & cache invalidation in a single location - */ -export class DataLoaders { - constructor (private ctx: DataContext) {} - - private _allLoaders: DataLoader[] = [] - - file (fileName: string) { - return this.fileLoader.load(fileName) - } - - maybeFile (fileName: string) { - return this.file(fileName).catch(() => null) - } - - jsonFile (fileName: string) { - return this.jsonFileLoader.load(fileName) as Promise - } - - projectConfig (projectRoot: string) { - return this.configLoader.load(projectRoot) - } - - gitInfo (path: string) { - return this.gitLoader.load(path) - } - - private gitLoader = this.loader((absolutePaths) => { - return getGitInfo(absolutePaths) - }) - - private configLoader = this.loader((projectRoots) => { - return Promise.all(projectRoots.map((root) => this.ctx._apis.projectApi.getConfig(root))) - }) - - private fileLoader = this.loader((files) => { - return Promise.all(files.map((f) => fs.readFile(f, 'utf8'))) - }) - - private jsonFileLoader = this.loader(async (jsonFiles) => { - const files = await this.fileLoader.loadMany(jsonFiles) - - return files.map((file) => { - if (file instanceof Error) { - return file - } - - try { - return JSON.parse(file) - } catch (e) { - return e - } - }) - }) - - private loader (batchLoadFn: DataLoader.BatchLoadFn) { - const loader = new DataLoader(batchLoadFn, { cache: false }) - - this._allLoaders.push(loader) - - return loader - } - - dispose () { - for (const loader of this._allLoaders) { - loader.clearAll() - } - } -} - -export function makeLoaders (ctx: DataContext) { - return new DataLoaders(ctx) -} diff --git a/packages/data-context/src/data/util/gitInfo.ts b/packages/data-context/src/data/util/gitInfo.ts deleted file mode 100644 index fbfb8e4818..0000000000 --- a/packages/data-context/src/data/util/gitInfo.ts +++ /dev/null @@ -1,105 +0,0 @@ -import execa from 'execa' -import path from 'path' -import os from 'os' -import Debug from 'debug' - -export interface GitInfo { - author: string | null - lastModifiedTimestamp: string | null - lastModifiedHumanReadable: string | null -} - -const debug = Debug('cypress:data-context:git-info') - -// matches -// $ git log -1 --pretty=format:%ci %ar %an -// eg '2021-09-14 13:43:19 +1000 2 days ago Lachlan Miller -const GIT_LOG_REGEXP = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+].+?)\s(.+ago)\s(.*)/ -const GIT_LOG_COMMAND = `git log -1 --pretty="format:%ci %ar %an"` - -const getInfoWindows = async (absolutePaths: readonly string[]) => { - const paths = absolutePaths.map((x) => path.resolve(x)).join(',') - const cmd = `FOR %x in (${paths}) DO (${GIT_LOG_COMMAND} %x)` - const result = await execa(cmd, { shell: true }) - - const split = result.stdout - .split('\r\n') // windows uses CRLF for carriage returns - .filter((str) => !str.includes('git log')) // windows stdout contains [cmd,output]. So we remove the code containing the executed command, `git log` - - // windows returns a leading carriage return, remove it - const [, ...stdout] = split - - if (stdout.length !== absolutePaths.length) { - debug('stdout', stdout) - throw Error(`Expect result array to have same length as input. Input: ${absolutePaths.length} Output: ${stdout.length}`) - } - - return stdout -} - -const getInfoPosix = async (absolutePaths: readonly string[]) => { - debug('getting git info for %o:', absolutePaths) - const paths = absolutePaths.map((x) => path.resolve(x)).join(',') - - // for file in {one,two} is valid in bash, but for file {one} is not - // no need to use a for loop for a single file - const cmd = absolutePaths.length === 1 - ? `${GIT_LOG_COMMAND} ${absolutePaths[0]}` - : `for file in {${paths}}; do echo $(${GIT_LOG_COMMAND} $file); done` - - debug('executing command `%s`:', cmd) - - const result = await execa(cmd, { shell: true }) - const stdout = result.stdout.split('\n') - - if (stdout.length !== absolutePaths.length) { - debug('error... stdout:', stdout) - throw Error(`Expect result array to have same length as input. Input: ${absolutePaths.length} Output: ${stdout.length}`) - } - - debug('stdout for git info', stdout) - - return stdout -} - -export const getGitInfo = async (absolutePaths: readonly string[]): Promise => { - if (absolutePaths.length === 0) { - return [] - } - - try { - const stdout = await ( - os.platform() === 'win32' - ? getInfoWindows(absolutePaths) - : getInfoPosix(absolutePaths) - ) - - const output: GitInfo[] = [] - - debug('stdout %s', stdout) - - for (let i = 0; i < absolutePaths.length; i++) { - const file = absolutePaths[i] - const data = stdout[i] - const info = data?.match(GIT_LOG_REGEXP) - - if (file && info && info[1] && info[2] && info[3]) { - output.push({ - lastModifiedTimestamp: info[1], - lastModifiedHumanReadable: info[2], - author: info[3], - }) - } - } - - return output - } catch (e) { - debug('Error getting git info: %s', e.message) - - // does not have git installed, - // file is not under source control - // ... etc ... - // just return an empty map - return [] - } -} diff --git a/packages/data-context/src/data/util/index.ts b/packages/data-context/src/data/util/index.ts deleted file mode 100644 index 59f3de886d..0000000000 --- a/packages/data-context/src/data/util/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable padding-line-between-statements */ -// created by autobarrel, do not modify directly - -export * from './gitInfo' diff --git a/packages/data-context/src/sources/FileDataSource.ts b/packages/data-context/src/sources/FileDataSource.ts new file mode 100644 index 0000000000..a43bb2ba85 --- /dev/null +++ b/packages/data-context/src/sources/FileDataSource.ts @@ -0,0 +1,47 @@ +import type { DataContext } from '..' + +export class FileDataSource { + private watchedFilePaths = new Set() + + constructor (private ctx: DataContext) {} + + readFile (absoluteFilePath: string) { + return this.fileLoader.load(absoluteFilePath).catch((e) => { + this.fileLoader.clear(absoluteFilePath) + throw e + }) + } + + readJsonFile (absoluteFilePath: string) { + return this.jsonFileLoader.load(absoluteFilePath).catch((e) => { + this.jsonFileLoader.clear(e) + throw e + }) as Promise + } + + private trackFile () { + // this.watchedFilePaths.clear() + // this.fileLoader.clear() + // this.jsonFileLoader.clear() + } + + private fileLoader = this.ctx.loader((files) => { + return this.ctx.util.settleAll(files.map((f) => this.ctx.fs.readFile(f, 'utf8'))) + }) + + private jsonFileLoader = this.ctx.loader(async (jsonFiles) => { + const files = await this.fileLoader.loadMany(jsonFiles) + + return files.map((file) => { + if (file instanceof Error) { + return file + } + + try { + return JSON.parse(file) + } catch (e) { + return e + } + }) + }) +} diff --git a/packages/data-context/src/sources/GitDataSource.ts b/packages/data-context/src/sources/GitDataSource.ts new file mode 100644 index 0000000000..6770a84c76 --- /dev/null +++ b/packages/data-context/src/sources/GitDataSource.ts @@ -0,0 +1,116 @@ +import execa from 'execa' +import path from 'path' +import os from 'os' + +import type { DataContext } from '..' + +// matches +// $ git log -1 --pretty=format:%ci %ar %an +// eg '2021-09-14 13:43:19 +1000 2 days ago Lachlan Miller +const GIT_LOG_REGEXP = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+].+?)\s(.+ago)\s(.*)/ +const GIT_LOG_COMMAND = `git log -1 --pretty="format:%ci %ar %an"` + +export interface GitInfo { + author: string | null + lastModifiedTimestamp: string | null + lastModifiedHumanReadable: string | null +} + +export class GitDataSource { + constructor (private ctx: DataContext) {} + + gitInfo (path: string): Promise { + return this.gitInfoLoader.load(path) + } + + private gitInfoLoader = this.ctx.loader((paths) => { + return this.bulkGitInfo(paths) + }) + + private async bulkGitInfo (absolutePaths: readonly string[]) { + if (absolutePaths.length === 0) { + return [] + } + + try { + const stdout = await ( + os.platform() === 'win32' + ? this.getInfoWindows(absolutePaths) + : this.getInfoPosix(absolutePaths) + ) + + const output: GitInfo[] = [] + + this.ctx.debug('stdout %s', stdout) + + for (let i = 0; i < absolutePaths.length; i++) { + const file = absolutePaths[i] + const data = stdout[i] + const info = data?.match(GIT_LOG_REGEXP) + + if (file && info && info[1] && info[2] && info[3]) { + output.push({ + lastModifiedTimestamp: info[1], + lastModifiedHumanReadable: info[2], + author: info[3], + }) + } + } + + return output + } catch (e) { + this.ctx.debug('Error getting git info: %s', (e as Error).message) + + // does not have git installed, + // file is not under source control + // ... etc ... + // just return an empty map + return [] + } + } + + private async getInfoPosix (absolutePaths: readonly string[]) { + this.ctx.debug('getting git info for %o:', absolutePaths) + const paths = absolutePaths.map((x) => path.resolve(x)).join(',') + + // for file in {one,two} is valid in bash, but for file {one} is not + // no need to use a for loop for a single file + const cmd = absolutePaths.length === 1 + ? `${GIT_LOG_COMMAND} ${absolutePaths[0]}` + : `for file in {${paths}}; do echo $(${GIT_LOG_COMMAND} $file); done` + + this.ctx.debug('executing command `%s`:', cmd) + + const result = await execa(cmd, { shell: true }) + const stdout = result.stdout.split('\n') + + if (stdout.length !== absolutePaths.length) { + this.ctx.debug('error... stdout:', stdout) + throw Error(`Expect result array to have same length as input. Input: ${absolutePaths.length} Output: ${stdout.length}`) + } + + this.ctx.debug('stdout for git info', stdout) + + return stdout + } + + private async getInfoWindows (absolutePaths: readonly string[]) { + const paths = absolutePaths.map((x) => path.resolve(x)).join(',') + const cmd = `FOR %x in (${paths}) DO (${GIT_LOG_COMMAND} %x)` + const result = await execa(cmd, { shell: true }) + + const split = result.stdout + .split('\r\n') // windows uses CRLF for carriage returns + .filter((str) => !str.includes('git log')) // windows stdout contains [cmd,output]. So we remove the code containing the executed command, `git log` + + // windows returns a leading carriage return, remove it + const [, ...stdout] = split + + if (stdout.length !== absolutePaths.length) { + this.ctx.debug('stdout', stdout) + throw Error(`Expect result array to have same length as input. Input: ${absolutePaths.length} Output: ${stdout.length}`) + } + + return stdout + } +} diff --git a/packages/data-context/src/sources/ProjectDataSource.ts b/packages/data-context/src/sources/ProjectDataSource.ts index 4d45fbab24..884395f5f1 100644 --- a/packages/data-context/src/sources/ProjectDataSource.ts +++ b/packages/data-context/src/sources/ProjectDataSource.ts @@ -1,3 +1,4 @@ +import type { FullConfig } from '@packages/types' import path from 'path' import type { DataContext } from '..' @@ -16,12 +17,16 @@ export class ProjectDataSource { } getConfig (projectRoot: string) { - return this.ctx.loaders.projectConfig(projectRoot) + return this.configLoader.load(projectRoot) } + private configLoader = this.ctx.loader((projectRoots) => { + return Promise.all(projectRoots.map((root) => this.ctx._apis.projectApi.getConfig(root))) + }) + async isFirstTimeAccessing (projectRoot: string, testingType: 'e2e' | 'component') { try { - const config = await this.ctx.loaders.jsonFile<{ e2e?: object, component?: object }>(path.join(projectRoot, 'cypress.json')) + const config = await this.ctx.file.readJsonFile<{ e2e?: object, component?: object }>(path.join(projectRoot, 'cypress.json')) const type = testingType === 'e2e' ? 'e2e' : 'component' const overrides = config[type] || {} diff --git a/packages/data-context/src/sources/UtilDataSource.ts b/packages/data-context/src/sources/UtilDataSource.ts new file mode 100644 index 0000000000..bee192d53c --- /dev/null +++ b/packages/data-context/src/sources/UtilDataSource.ts @@ -0,0 +1,38 @@ +import DataLoader from 'dataloader' +import type { DataContext } from '..' + +/** + * this.ctx.util.... + * + * Used as a central location for grab-bag utilities used + * within the DataContext layer + */ +export class UtilDataSource { + constructor (private ctx: DataContext) {} + + private _allLoaders: DataLoader[] = [] + + async settleAll (promises: Promise[]) { + const vals = await Promise.allSettled(promises) + + return vals.map((v) => v.status === 'fulfilled' ? v.value : this.ensureError(v.reason)) + } + + ensureError (val: any): Error { + return val instanceof Error ? val : new Error(val) + } + + loader = (batchLoadFn: DataLoader.BatchLoadFn) => { + const loader = new DataLoader(batchLoadFn, { cache: false }) + + this._allLoaders.push(loader) + + return loader + } + + disposeLoaders () { + for (const loader of this._allLoaders) { + loader.clearAll() + } + } +} diff --git a/packages/data-context/src/sources/index.ts b/packages/data-context/src/sources/index.ts index 90dd4fe190..f7601ac673 100644 --- a/packages/data-context/src/sources/index.ts +++ b/packages/data-context/src/sources/index.ts @@ -3,5 +3,8 @@ export * from './AppDataSource' export * from './BrowserDataSource' +export * from './FileDataSource' +export * from './GitDataSource' export * from './ProjectDataSource' +export * from './UtilDataSource' export * from './WizardDataSource' diff --git a/packages/data-context/tsconfig.json b/packages/data-context/tsconfig.json index 2dc35b9605..f80b4eb382 100644 --- a/packages/data-context/tsconfig.json +++ b/packages/data-context/tsconfig.json @@ -6,6 +6,7 @@ "script" ], "compilerOptions": { + "lib": ["es2020"], "importHelpers": true, "strict": true, "allowJs": false, diff --git a/packages/frontend-shared/cypress/plugins/index.js b/packages/frontend-shared/cypress/plugins/index.js index 58ca13b0d0..519a4f5893 100644 --- a/packages/frontend-shared/cypress/plugins/index.js +++ b/packages/frontend-shared/cypress/plugins/index.js @@ -31,7 +31,14 @@ module.exports = (on, config) => { viteConfig: { // TODO(tim): Figure out why this isn't being picked up optimizeDeps: { - include: ['@headlessui/vue', 'vue-prism-component', 'vue3-file-selector'], + include: [ + '@headlessui/vue', + 'vue-prism-component', + 'vue3-file-selector', + 'just-my-luck', + 'combine-properties', + 'faker', + ], }, }, }) diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index 7625aa3030..b92a8ebbc8 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -51,6 +51,7 @@ "vue": "3.2.6", "vue-eslint-parser": "7.11.0", "vue-i18n": "^9.2.0-beta.7", + "vue-toast-notification": "2.0.1", "vue-tsc": "^0.3.0", "windicss": "3.1.8", "windicss-analysis": "^0.3.4", diff --git a/packages/frontend-shared/src/graphql/urqlClient.ts b/packages/frontend-shared/src/graphql/urqlClient.ts index c8b3f9ae44..d686e8a9cf 100644 --- a/packages/frontend-shared/src/graphql/urqlClient.ts +++ b/packages/frontend-shared/src/graphql/urqlClient.ts @@ -7,9 +7,21 @@ import { fetchExchange, } from '@urql/core' import { devtoolsExchange } from '@urql/devtools' +import VueToast, { ToastPluginApi } from 'vue-toast-notification' +import 'vue-toast-notification/dist/theme-sugar.css' + +export { VueToast } + import { cacheExchange as graphcacheExchange } from '@urql/exchange-graphcache' + import { GRAPHQL_URL } from '../utils/env' +declare global { + interface Window { + $app?: { $toast: ToastPluginApi } + } +} + export function makeCacheExchange () { return graphcacheExchange({ keys: { @@ -22,10 +34,19 @@ export function makeCacheExchange () { export function makeUrqlClient (): Client { const exchanges: Exchange[] = [ - devtoolsExchange, dedupExchange, errorExchange({ onError (error) { + const message = ` + GraphQL Field Path: [${error.graphQLErrors[0].path?.join(', ')}]:
+ ${error.message}
+ ` + + window.$app?.$toast.error(message, { + message, + duration: 0, + }) + // eslint-disable-next-line console.error(error) }, diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 820741dfa7..4d6cd96d97 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -464,6 +464,11 @@ type Spec implements Node { """ absolute: String! + """ + Full name of spec file (e.g. MySpec.test.tsx) without the spec extension + """ + baseName: String! + """The file extension (e.g. tsx, jsx)""" fileExtension: String! diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Spec.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Spec.ts index fa9342c6d4..b6cc560aed 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Spec.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Spec.ts @@ -44,7 +44,7 @@ export const Spec = objectType({ type: GitInfo, description: 'Git information about the spec file', resolve: async (source, args, ctx) => { - return ctx.loaders.gitInfo(source.absolute) + return ctx.git.gitInfo(source.absolute) }, }) }, diff --git a/packages/graphql/tsconfig.json b/packages/graphql/tsconfig.json index 4958c373bd..c2a9e02f61 100644 --- a/packages/graphql/tsconfig.json +++ b/packages/graphql/tsconfig.json @@ -9,10 +9,12 @@ "script" ], "compilerOptions": { + "lib": ["ES2020"], "importHelpers": true, "strict": true, "allowJs": false, "noImplicitAny": true, + "noUnusedLocals": false, "resolveJsonModule": true, "experimentalDecorators": true, "noUncheckedIndexedAccess": true, diff --git a/packages/launchpad/cypress/component/plugins/index.js b/packages/launchpad/cypress/component/plugins/index.js index a4615f1780..1227b15a66 100644 --- a/packages/launchpad/cypress/component/plugins/index.js +++ b/packages/launchpad/cypress/component/plugins/index.js @@ -1,8 +1,3 @@ -/** - * @type {import('@cypress/vite-dev-server')} - */ -const { startDevServer } = require('@cypress/vite-dev-server') - /// // *********************************************************** // This example plugins/index.js can be used to load plugins @@ -20,23 +15,4 @@ const { startDevServer } = require('@cypress/vite-dev-server') * @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 - - if (config.testingType === 'component') { - on('dev-server:start', async (options) => { - return startDevServer({ - options, - viteConfig: { - // TODO(tim): Figure out why this isn't being picked up - optimizeDeps: { - include: ['@headlessui/vue', 'vue-prism-component'], - }, - }, - }) - }) - } - - return config // IMPORTANT to return a config -} +module.exports = require('@packages/frontend-shared/cypress/plugins/index') diff --git a/packages/launchpad/src/main.ts b/packages/launchpad/src/main.ts index d130abd379..2666cf77ad 100644 --- a/packages/launchpad/src/main.ts +++ b/packages/launchpad/src/main.ts @@ -3,12 +3,13 @@ import './main.scss' import 'virtual:windi.css' import urql from '@urql/vue' import App from './App.vue' -import { makeUrqlClient } from '@packages/frontend-shared/src/graphql/urqlClient' +import { makeUrqlClient, VueToast } from '@packages/frontend-shared/src/graphql/urqlClient' import { createI18n } from '@cy/i18n' const app = createApp(App) +app.use(VueToast) app.use(urql, makeUrqlClient()) app.use(createI18n()) -app.mount('#app') +window.$app = app.mount('#app') diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index cea924034a..d1e63c1de9 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -1464,7 +1464,8 @@ module.exports = { socketId: options.socketId, webSecurity: options.webSecurity, projectRoot: options.projectRoot, - }, options.testingType === 'e2e' || firstSpec), + // TODO(tim): investigate the socket disconnect + }, process.env.CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH || options.testingType === 'e2e' || firstSpec), }) }) }, diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 87e0556e55..4fe3afe14e 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -15,6 +15,8 @@ import { openFile } from './util/file-opener' import open from './util/open' import type { DestroyableHttpServer } from './util/server_destroy' import * as session from './session' +// eslint-disable-next-line no-duplicate-imports +import type { Socket } from '@packages/socket' type StartListeningCallbacks = { onSocketConnection: (socket: any) => void @@ -184,9 +186,21 @@ export class SocketBase { const getFixture = (path, opts) => fixture.get(config.fixturesFolder, path, opts) - this.io.on('connection', (socket: any) => { + this.io.on('connection', (socket: Socket & { inReporterRoom?: boolean, inRunnerRoom?: boolean }) => { debug('socket connected') + socket.on('disconnecting', (reason) => { + debug(`socket-disconnecting ${reason}`) + }) + + socket.on('disconnect', (reason) => { + debug(`socket-disconnect ${reason}`) + }) + + socket.on('error', (err) => { + debug(`socket-error ${err.message}`) + }) + // cache the headers so we can access // them at any time const headers = socket.request?.headers ?? {} diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index fd8eca864f..7e30aed0df 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -8,8 +8,10 @@ "./../ts/index.d.ts" ], "compilerOptions": { + "lib": ["ES2020"], "types": ["mocha", "node"], "importHelpers": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "noUnusedLocals": false, } } diff --git a/packages/socket/lib/socket.ts b/packages/socket/lib/socket.ts index e93099df99..f88980be5a 100644 --- a/packages/socket/lib/socket.ts +++ b/packages/socket/lib/socket.ts @@ -1,8 +1,10 @@ import fs from 'fs' import type http from 'http' -import server, { Server as SocketIOBaseServer, ServerOptions } from 'socket.io' +import server, { Server as SocketIOBaseServer, ServerOptions, Socket } from 'socket.io' import { client } from './browser' +export type { Socket } + const HUNDRED_MEGABYTES = 1e8 // 100000000 const { version } = require('socket.io-client/package.json') diff --git a/yarn.lock b/yarn.lock index 145783c8f6..e8e15a5c1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8488,6 +8488,13 @@ resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.28.tgz#c054e8af4d9dd75db4e63abc76f885168714d4b3" integrity sha1-wFTor02d11205jq8dviFFocU1LM= +"@types/fluent-ffmpeg@^2.1.18": + version "2.1.18" + resolved "https://registry.yarnpkg.com/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.18.tgz#72246c2f8c0f0a590aefc1cdbe13736c73f22a81" + integrity sha512-LTteOx3RUmnPlKkvhvW9aGOHdJYyEtIiRBVbYVO/zPU65ZYQelbPJ+zBBT+IXup7doMvxVstX7NMoUTWKZOhww== + dependencies: + "@types/node" "*" + "@types/fs-extra@^8.0.1": version "8.1.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.1.tgz#1e49f22d09aa46e19b51c0b013cb63d0d923a068" @@ -41699,6 +41706,11 @@ vue-template-es2015-compiler@^1.9.0, vue-template-es2015-compiler@^1.9.1: resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== +vue-toast-notification@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vue-toast-notification/-/vue-toast-notification-2.0.1.tgz#5fe607c493b5dc9b238bf49bc3bcf80366edd4e4" + integrity sha512-8GPJq1J6lsTPTCxSgPhnM8d0v+ivwT+u4R/xmtaDXeYNRkaRxqFOn3ewaVqmm+aCT8Y3/PQQS8dFqkMV7JOf/A== + vue-tsc@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-0.3.0.tgz#3b3872bf4f1d2e4409b57adbd826032e253db406"