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

View File

@@ -11,32 +11,18 @@ export const ENV_VARS = {
PROD: {
CYPRESS_INTERNAL_ENV: 'production',
CYPRESS_INTERNAL_CLOUD_ENV: 'production',
CYPRESS_INTERNAL_GQL_PORT: `52200`,
},
// Used when we're spawning Cypress as the E2E target for the
// test runner. We build the assets w/ GQL_TEST_PORT into `dist-e2e`,
// and spawn the server against
E2E_TEST_TARGET: {
CYPRESS_INTERNAL_CLOUD_ENV: DEFAULT_INTERNAL_CLOUD_ENV, // staging for now, until we get an e2e workflow w/ cloud project
CYPRESS_INTERNAL_GQL_PORT: `52300`,
CYPRESS_INTERNAL_ENV: 'staging', // Different than DEV, which will default to "development". TODO: Make this do less things internall
CYPRESS_INTERNAL_E2E_TESTING_SELF: `true`,
XVFB_DISPLAY_NUM: `44`,
},
// Uses the "built" vite assets, not the served ones
DEV_OPEN: {
CYPRESS_KONFIG_ENV: DEFAULT_INTERNAL_CLOUD_ENV, // TODO: Change this / remove konfig
CYPRESS_INTERNAL_CLOUD_ENV: DEFAULT_INTERNAL_CLOUD_ENV, // staging for now, until we get an e2e workflow w/ cloud project
CYPRESS_INTERNAL_GQL_PORT: `52200`,
},
// Used when we're running Cypress in true "development" mode
DEV: {
CYPRESS_KONFIG_ENV: DEFAULT_INTERNAL_CLOUD_ENV, // TODO: Change this / remove konfig
CYPRESS_INTERNAL_CLOUD_ENV: DEFAULT_INTERNAL_CLOUD_ENV, // staging for now, until we get an e2e workflow w/ cloud project
CYPRESS_INTERNAL_GQL_PORT: `52200`,
CYPRESS_INTERNAL_VITE_APP_PORT: `3333`,
CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT: `3001`,
},

View File

@@ -9,32 +9,33 @@
import gulp from 'gulp'
import { autobarrelWatcher } from './tasks/gulpAutobarrel'
import { startCypressWatch, startCypressForTest, openCypressLaunchpad, openCypressApp, runCypressLaunchpad, wrapRunWithExit, runCypressApp, waitForTestGraphQLApi, killExistingCypress } from './tasks/gulpCypress'
import { startCypressWatch, openCypressLaunchpad, openCypressApp, runCypressLaunchpad, wrapRunWithExit, runCypressApp, killExistingCypress } from './tasks/gulpCypress'
import { graphqlCodegen, graphqlCodegenWatch, nexusCodegen, nexusCodegenWatch, generateFrontendSchema, syncRemoteGraphQL } from './tasks/gulpGraphql'
import { viteApp, viteBuildAndWatchLaunchpadForTest, viteBuildLaunchpadForTest, serveBuiltLaunchpadForTest, viteCleanApp, viteCleanLaunchpad, viteLaunchpad, serveBuiltAppForTest, viteBuildAppForTest, viteBuildAndWatchAppForTest, viteBuildApp, viteBuildLaunchpad } from './tasks/gulpVite'
import { viteApp, viteCleanApp, viteCleanLaunchpad, viteLaunchpad, viteBuildApp, viteBuildAndWatchApp, viteBuildLaunchpad, viteBuildAndWatchLaunchpad } from './tasks/gulpVite'
import { checkTs } from './tasks/gulpTsc'
import { makePathMap } from './utils/makePathMap'
import { makePackage } from './tasks/gulpMakePackage'
import { setGulpGlobal } from './gulpConstants'
import { exitAfterAll } from './tasks/gulpRegistry'
import { execSync } from 'child_process'
import { webpackRunner } from './tasks/gulpWebpack'
/**------------------------------------------------------------------------
* Local Development Workflow
* * `yarn dev` is your primary command for getting work done
*------------------------------------------------------------------------**/
gulp.task('viteClean', gulp.parallel(
viteCleanApp,
viteCleanLaunchpad,
))
gulp.task(
'codegen',
gulp.series(
// Autobarrel watcher
autobarrelWatcher,
// Clean any vite assets
gulp.parallel(
viteCleanApp,
viteCleanLaunchpad,
),
// Codegen for our GraphQL Server so we have the latest schema to build
// the frontend codegen correctly
// Fetch the latest "remote" schema from the Cypress cloud
@@ -48,25 +49,50 @@ gulp.task(
),
)
gulp.task(
'commonSetup',
gulp.series(
'codegen',
killExistingCypress,
),
)
gulp.task(
'dev',
gulp.series(
'codegen',
killExistingCypress,
// Now that we have the codegen, we can start the frontend(s)
gulp.parallel(
viteApp,
viteLaunchpad,
webpackRunner,
),
killExistingCypress,
// And we're finally ready for electron, watching for changes in
// /graphql to auto-restart the server
startCypressWatch,
),
)
gulp.task(
'dev:no-watch',
gulp.series(
async function setNoWatch () {
setGulpGlobal('shouldWatch', false)
},
'dev',
),
)
gulp.task('dev:clean', gulp.series(
// Clean any vite assets
'viteClean',
'dev',
))
gulp.task(
'debug',
gulp.series(
@@ -94,10 +120,7 @@ gulp.task(
gulp.task('buildProd',
gulp.series(
gulp.parallel(
viteCleanApp,
viteCleanLaunchpad,
),
'viteClean',
syncRemoteGraphQL,
nexusCodegen,
@@ -135,22 +158,9 @@ gulp.task('cyRunLaunchpadE2E', gulp.series(
// 1. Build the Cypress App itself
'buildProd',
// 2. Build the Launchpad under test.
viteBuildLaunchpadForTest,
// 3. Host the Launchpad on a static server for cy.visit.
serveBuiltLaunchpadForTest,
// Ensure we have no existing cypress processes running
killExistingCypress,
// 4. Start the TEST Cypress App, such that its ports and other globals
// don't conflict with the real Cypress App.
startCypressForTest,
// Wait for the Test Cypress open to start
waitForTestGraphQLApi,
// 5. Start the REAL Cypress App, which will execute the integration specs.
async function _runCypressLaunchpad () {
wrapRunWithExit(await runCypressLaunchpad())
@@ -161,24 +171,8 @@ gulp.task('cyRunAppE2E', gulp.series(
// 1. Build the Cypress App itself
'buildProd',
// 2. Build the Launchpad under test.
gulp.parallel(
viteBuildLaunchpadForTest,
viteBuildAppForTest,
),
// 3. Host the Launchpad on a static server for cy.visit.
serveBuiltAppForTest,
killExistingCypress,
// 4. Start the TEST Cypress App, such that its ports and other globals
// don't conflict with the real Cypress App.
startCypressForTest,
// Wait for the Test Cypress open to start
waitForTestGraphQLApi,
// 5. Start the REAL Cypress App, which will execute the integration specs.
async function _runCypressApp () {
wrapRunWithExit(await runCypressApp())
@@ -186,47 +180,41 @@ gulp.task('cyRunAppE2E', gulp.series(
))
const cyOpenLaunchpad = gulp.series(
// 2. Build + watch Launchpad under test.
// 1. Build + watch Launchpad under test.
// This watches for changes and is not the same things as statically
// building the app for production.
viteBuildAndWatchLaunchpadForTest,
gulp.parallel(
viteBuildApp,
viteBuildAndWatchLaunchpad,
),
// 4. Start the TEST Cypress App, such that its ports and other globals
// don't conflict with the real Cypress App.
startCypressForTest,
// 3. Host the Launchpad on a static server for cy.visit.
serveBuiltLaunchpadForTest,
// 5. Start the REAL (dev) Cypress App, which will launch in open mode.
// 2. Start the REAL (dev) Cypress App, which will launch in open mode.
openCypressLaunchpad,
)
const cyOpenApp = gulp.series(
// 2. Build + watch Launchpad under test.
// 1. Build + watch Launchpad under test.
// This watches for changes and is not the same things as statically
// building the app for production.
gulp.parallel(
viteBuildLaunchpadForTest,
viteBuildAndWatchAppForTest,
gulp.series(
viteBuildAndWatchLaunchpad,
viteBuildAndWatchApp,
),
webpackRunner,
),
// 3. Start the TEST Cypress App, such that its ports and other globals
// don't conflict with the real Cypress App.
startCypressForTest,
// 4. Host the Launchpad on a static server for cy.visit.
serveBuiltAppForTest,
// 5. Start the REAL (dev) Cypress App, which will launch in open mode.
// 2. Start the REAL (dev) Cypress App, which will launch in open mode.
openCypressApp,
)
// Open Cypress in production mode.
// Rebuild the Launchpad app between changes.
gulp.task('cyOpenLaunchpadE2E', gulp.series(
'viteClean',
// 1. Build the Cypress App itself
'buildProd',
'commonSetup',
// 2. Open the "app"
cyOpenLaunchpad,
@@ -235,8 +223,10 @@ gulp.task('cyOpenLaunchpadE2E', gulp.series(
// Open Cypress in production mode.
// Rebuild the Launchpad app between changes.
gulp.task('cyOpenAppE2E', gulp.series(
'viteClean',
// 1. Build the Cypress App itself
'buildProd',
'commonSetup',
// 2. Open the launchpad app
cyOpenApp,
@@ -267,22 +257,14 @@ gulp.task(nexusCodegen)
gulp.task(nexusCodegenWatch)
gulp.task(graphqlCodegen)
gulp.task(graphqlCodegenWatch)
gulp.task(startCypressForTest)
gulp.task(viteCleanApp)
gulp.task(viteCleanLaunchpad)
gulp.task(viteBuildLaunchpadForTest)
gulp.task(viteBuildAppForTest)
gulp.task(serveBuiltAppForTest)
gulp.task(serveBuiltLaunchpadForTest)
gulp.task(viteBuildAndWatchLaunchpadForTest)
gulp.task(viteBuildAndWatchAppForTest)
gulp.task(viteBuildApp)
gulp.task(viteBuildLaunchpad)
gulp.task(viteBuildAndWatchApp)
gulp.task(viteBuildAndWatchLaunchpad)
gulp.task('debugCypressLaunchpad', gulp.series(
async function setupDebugBrk () {

View File

@@ -4,8 +4,6 @@
*
* @summary Gulp tasks to run the Cypress app.
*/
// @ts-expect-error - no types
import rp from '@cypress/request-promise'
import chokidar from 'chokidar'
import path from 'path'
import pDefer from 'p-defer'
@@ -33,23 +31,6 @@ export async function killExistingCypress () {
child.on('exit', dfd.resolve)
}
export async function waitForTestGraphQLApi () {
let i = 0
// eslint-disable-next-line no-constant-condition
while (true) {
try {
return await rp.get('http://localhost:52300/graphql?query={__typename}')
} catch (e) {
if (i++ > 10) {
throw e
}
await new Promise((resolve) => setTimeout(resolve, 2000))
}
}
}
export async function openCypressLaunchpad () {
return spawnCypressWithMode('open', 'dev', ENV_VARS.DEV_OPEN, ['--project', monorepoPaths.pkgLaunchpad])
}
@@ -70,23 +51,6 @@ export async function runCypressProd () {
return spawnCypressWithMode('run', 'prod', ENV_VARS.PROD)
}
/**------------------------------------------------------------------------
* Testing Tasks
* Building and running the Cypress app and graphql server for testing.
* * startCypressForTest - Start the Cypress server, but without watching
* * runCypressAgainstDist - Serve the dist'd frontend over file://
*------------------------------------------------------------------------**/
// Use the GQL Test Port (52300 by default, defined in ./gulp/gulpConstants)
// Spawns Cypress in "Test Cypress within Cypress" mode
export async function startCypressForTest () {
return spawnCypressWithMode('open', 'test', ENV_VARS.E2E_TEST_TARGET)
}
export async function runCypressAgainstDist () {
return spawnCypressWithMode('run', 'test', ENV_VARS.E2E_TEST_TARGET)
}
/**------------------------------------------------------------------------
* Start and Watch Utils
* * spawnCypressWithMode - Formerly known as: `node ./scripts/cypress.js run`
@@ -126,6 +90,7 @@ async function spawnCypressWithMode (
...process.env,
...env,
LAUNCHPAD: '1',
TS_NODE_COMPILER: 'typescript-cached-transpile',
}
return await forked(`cy:${mode}:${type}`, pathToCli, [mode, ...argv], {
@@ -142,15 +107,6 @@ async function spawnCypressWithMode (
*------------------------------------------------------------------------**/
export async function startCypressWatch () {
const watcher = chokidar.watch([
'packages/{graphql,data-context}/src/**/*.{js,ts}',
'packages/server/lib/graphql/**/*.{js,ts}',
], {
cwd: monorepoPaths.root,
ignored: /\.gen\.ts/,
ignoreInitial: true,
})
let isClosing = false
let isRestarting = false
let child: ChildProcess | null = null
@@ -194,15 +150,26 @@ export async function startCypressWatch () {
isRestarting = false
}
watcher.on('add', restartServer)
watcher.on('change', restartServer)
if (getGulpGlobal('shouldWatch')) {
const watcher = chokidar.watch([
'packages/{graphql,data-context}/src/**/*.{js,ts}',
'packages/server/lib/**/*.{js,ts}',
], {
cwd: monorepoPaths.root,
ignored: /\.gen\.ts/,
ignoreInitial: true,
})
watcher.on('add', restartServer)
watcher.on('change', restartServer)
process.on('beforeExit', () => {
isClosing = true
watcher.close()
})
}
await startCypressWithListeners()
process.on('beforeExit', () => {
isClosing = true
watcher.close()
})
}
export function wrapRunWithExit (proc: ChildProcess) {

View File

@@ -68,9 +68,8 @@ export async function makePackage () {
'test-integration': 'mocha -r @packages/ts/register test/integration/**/*.spec.ts --config ./test/.mocharc.js --exit',
} : {}),
},
dependencies: {
'tslib': '2.3.0',
},
files: ['src'],
dependencies: {},
devDependencies: results.scaffoldTests ? {
'mocha': '7.0.1',
'chai': '4.2.0',
@@ -94,7 +93,6 @@ export async function makePackage () {
'script',
],
'compilerOptions': {
'importHelpers': true,
'strict': true,
'allowJs': false,
'rootDir': 'src',

View File

@@ -1,6 +1,7 @@
import type { ChildProcess } from 'child_process'
import pDefer from 'p-defer'
import treeKill from 'tree-kill'
import gulp from 'gulp'
const childProcesses = new Set<ChildProcess>()
const exitedPids = new Set<number>()
@@ -53,6 +54,19 @@ export async function exitAllProcesses () {
process.stdin.resume() //so the program will not close instantly
const _task = gulp.task
// So that we auto-exit on single tasks
// @ts-expect-error
gulp.task = function () {
if (arguments.length === 1 && typeof arguments[0] === 'function') {
process.stdin.pause()
}
// @ts-ignore
return _task.apply(this, arguments)
}
export async function exitAfterAll () {
process.stdin.pause()
}

View File

@@ -22,26 +22,18 @@ import { AllSpawnableApps, spawned, spawnUntilMatch } from '../utils/childProces
*------------------------------------------------------------------------**/
export function viteApp () {
const GQL_PORT = ENV_VARS.DEV.CYPRESS_INTERNAL_GQL_PORT
const APP_PORT = ENV_VARS.DEV.CYPRESS_INTERNAL_VITE_APP_PORT
return spawnViteDevServer('vite-app', `yarn vite --port ${APP_PORT} --base /__vite__/`, {
cwd: monorepoPaths.pkgApp,
env: {
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
},
})
}
export function viteLaunchpad () {
const GQL_PORT = ENV_VARS.DEV.CYPRESS_INTERNAL_GQL_PORT
const LAUNCHPAD_PORT = ENV_VARS.DEV.CYPRESS_INTERNAL_VITE_LAUNCHPAD_PORT
return spawnViteDevServer('vite-launchpad', `yarn vite --port ${LAUNCHPAD_PORT}`, {
cwd: monorepoPaths.pkgLaunchpad,
env: {
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
},
})
}
@@ -85,7 +77,17 @@ export function viteBuildApp () {
cwd: monorepoPaths.pkgApp,
waitForExit: true,
env: {
...process.env,
// ...process.env,
NODE_ENV: 'production',
},
})
}
export function viteBuildAndWatchApp () {
return watchViteBuild('vite:build-watch-app', `yarn vite build --watch`, {
cwd: monorepoPaths.pkgApp,
env: {
// ...process.env,
NODE_ENV: 'production',
},
})
@@ -95,10 +97,12 @@ export function viteBuildLaunchpad () {
return spawned('vite:build-launchpad', `yarn vite build`, {
cwd: monorepoPaths.pkgLaunchpad,
waitForExit: true,
env: {
...process.env,
NODE_ENV: 'production',
},
})
}
export function viteBuildAndWatchLaunchpad () {
return watchViteBuild('vite:build-watch-launchpad', `yarn vite build --watch`, {
cwd: monorepoPaths.pkgLaunchpad,
})
}
@@ -115,84 +119,3 @@ export function viteCleanLaunchpad () {
waitForExit: true,
})
}
/**------------------------------------------------------------------------
* Testing Tasks
* Build and serve the Vite frontend(s) as web apps on a static server.
* * viteBuildLaunchpadForTest
* * viteBuildAppForTest
* * serveBuiltLaunchpadForTest
* * serveBuiltAppForTest
*------------------------------------------------------------------------**/
// After running `serveBuiltLaunchpadForTest`, you're able to visit
// `http://localhost:5555` to access the Launchpad frontend.
export function serveBuiltLaunchpadForTest () {
return spawnUntilMatch('serve:launchpad-for-test', {
command: `yarn serve ./dist-e2e -p 5555`,
match: 'Accepting connections',
options: {
cwd: monorepoPaths.pkgLaunchpad,
},
})
}
export function viteBuildLaunchpadForTest () {
const GQL_PORT = ENV_VARS.E2E_TEST_TARGET.CYPRESS_INTERNAL_GQL_PORT
return spawned('vite:build-launchpad-for-test', `yarn vite build --outDir=./dist-e2e`, {
cwd: monorepoPaths.pkgLaunchpad,
waitForExit: true,
env: {
NODE_ENV: 'production',
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
},
})
}
export async function viteBuildAndWatchLaunchpadForTest () {
const GQL_PORT = ENV_VARS.E2E_TEST_TARGET.CYPRESS_INTERNAL_GQL_PORT
return watchViteBuild('vite:build-watch-launchpad-for-test', `yarn vite build --watch --outDir=./dist-e2e`, {
cwd: monorepoPaths.pkgLaunchpad,
env: {
NODE_ENV: 'production',
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
},
})
}
/**----------------------
*todo Implement E2E tests for the App.
*------------------------**/
export function viteBuildAppForTest () {
const GQL_PORT = ENV_VARS.E2E_TEST_TARGET.CYPRESS_INTERNAL_GQL_PORT
return spawned('vite:build-app-for-test', `yarn vite build --outDir=./dist-e2e`, {
cwd: monorepoPaths.pkgApp,
waitForExit: true,
env: {
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
...process.env,
},
})
}
export function serveBuiltAppForTest () {
return spawned('serve:app-for-test', `yarn serve ./dist-e2e -p 5556`, {
cwd: monorepoPaths.pkgApp,
})
}
export async function viteBuildAndWatchAppForTest () {
const GQL_PORT = ENV_VARS.E2E_TEST_TARGET.CYPRESS_INTERNAL_GQL_PORT
return watchViteBuild('vite:build-watch-app-for-test', `yarn vite build --watch --outDir=./dist-e2e`, {
cwd: monorepoPaths.pkgApp,
env: {
NODE_ENV: 'production',
VITE_CYPRESS_INTERNAL_GQL_PORT: GQL_PORT,
},
})
}

View File

@@ -0,0 +1,70 @@
import chalk from 'chalk'
import { spawn } from 'child_process'
import pDefer from 'p-defer'
import { monorepoPaths } from '../monorepoPaths'
import { addChildProcess } from './gulpRegistry'
export function webpackRunner () {
return runWebpack({
cwd: monorepoPaths.pkgRunner,
prefix: 'webpack:runner',
args: ['-w'],
})
}
type RunWebpackCfg = {
cwd: string
prefix: string
args?: string[]
env?: object
devServer?: boolean
}
export async function runWebpack (cfg: RunWebpackCfg) {
const { cwd, args = [], env = process.env, devServer = false, prefix } = cfg
const dfd = pDefer()
const spawned = spawn(
devServer
? './node_modules/.bin/webpack-dev-server'
: './node_modules/.bin/webpack',
args,
{
cwd,
env: {
...(env || process.env),
FORCE_COLOR: '1',
},
},
)
addChildProcess(spawned)
spawned.stdout.on('data', (chunk) => {
process.stdout.write('\n')
String(chunk)
.split('\n')
.forEach((line) => {
if (
line.includes('Compiled successfully') ||
line.includes('Compiled with warnings') ||
line.includes('Failed to compile') ||
line.includes('Built at: ')
) {
dfd.resolve({})
}
process.stdout.write(`${chalk.cyan(`${prefix}: `)}${line}\n`)
})
})
spawned.stderr.on('data', (chunk) => {
process.stderr.write('\n')
String(chunk)
.split('\n')
.forEach((line) => {
process.stderr.write(`${chalk.red(`${prefix}: `)}${line}\n`)
})
})
return dfd.promise
}