Skip to content

Commit

Permalink
feat: ProjectLifecycleManager & general launchpad cleanup (#19347)
Browse files Browse the repository at this point in the history
See #19347 for full summary

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
Co-authored-by: estrada9166 <estrada9166@hotmail.com>
Co-authored-by: Alejandro Estrada <estrada9166@gmail.com>
Co-authored-by: Jess <jess@jessicasachs.io>
  • Loading branch information
5 people committed Dec 22, 2021
1 parent 67c42fc commit 4626f74
Show file tree
Hide file tree
Showing 217 changed files with 4,335 additions and 4,601 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@

// Volar is the main extension that powers Vue's language features.
"volar.autoCompleteRefs": false,
"volar.takeOverMode.enabled": true
// "volar.takeOverMode.enabled": true
}
11 changes: 6 additions & 5 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ macWorkflowFilters: &mac-workflow-filters
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ '10.0-release', << pipeline.git.branch >> ]
- equal: [ 'tgriesser/10.0-release/refactor-lifecycle-ui', << pipeline.git.branch >> ]
- equal: [ renovate/cypress-request-2.x, << pipeline.git.branch >> ]
- matches:
pattern: "-release$"
Expand All @@ -49,6 +50,7 @@ windowsWorkflowFilters: &windows-workflow-filters
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ '10.0-release', << pipeline.git.branch >> ]
- equal: [ 'tgriesser/10.0-release/refactor-lifecycle-ui', << pipeline.git.branch >> ]
- equal: [ test-binary-downstream-windows, << pipeline.git.branch >> ]
- matches:
pattern: "-release$"
Expand Down Expand Up @@ -618,7 +620,6 @@ commands:
command: |
git fetch origin pull/<<parameters.pull_request_id>>/head:pr-<<parameters.pull_request_id>>
git checkout pr-<<parameters.pull_request_id>>
git log -n 2
test-binary-against-rwa:
description: |
Expand Down Expand Up @@ -1334,7 +1335,7 @@ jobs:

run-launchpad-integration-tests-chrome:
<<: *defaults
parallelism: 1
parallelism: 2
steps:
- run-new-ui-tests:
browser: chrome
Expand All @@ -1344,7 +1345,7 @@ jobs:

run-app-component-tests-chrome:
<<: *defaults
parallelism: 1
parallelism: 7
steps:
- run-new-ui-tests:
browser: chrome
Expand All @@ -1354,7 +1355,7 @@ jobs:

run-app-integration-tests-chrome:
<<: *defaults
parallelism: 1
parallelism: 2
steps:
- run-new-ui-tests:
browser: chrome
Expand Down Expand Up @@ -1668,7 +1669,7 @@ jobs:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "test-binary-downstream-windows" && "$CIRCLE_BRANCH" != "10.0-release" && "$CIRCLE_BRANCH" != "renovate/cypress-request-2.x" && "$CIRCLE_BRANCH" != "tgriesser/fix/patch-resolutions" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "test-binary-downstream-windows" && "$CIRCLE_BRANCH" != "tgriesser/10.0-release/refactor-lifecycle-ui" && "$CIRCLE_BRANCH" != "10.0-release" && "$CIRCLE_BRANCH" != "renovate/cypress-request-2.x" && "$CIRCLE_BRANCH" != "tgriesser/fix/patch-resolutions" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
Expand Down
18 changes: 14 additions & 4 deletions cli/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,16 @@ const addCypressOpenCommand = (program) => {
.option('--dev', text('dev'), coerceFalse)
}

const maybeAddInspectFlags = (program) => {
if (process.argv.includes('--dev')) {
return program
.option('--inspect', 'Node option')
.option('--inspect-brk', 'Node option')
}

return program
}

/**
* Casts known command line options for "cypress run" to their intended type.
* For example if the user passes "--port 5005" the ".port" property should be
Expand Down Expand Up @@ -336,7 +346,7 @@ module.exports = {
debug('creating program parser')
const program = createProgram()

addCypressRunCommand(program)
maybeAddInspectFlags(addCypressRunCommand(program))
.action((...fnArgs) => {
debug('parsed Cypress run %o', fnArgs)
const options = parseVariableOpts(fnArgs, cliArgs)
Expand Down Expand Up @@ -377,7 +387,7 @@ module.exports = {
debug('creating program parser')
const program = createProgram()

addCypressOpenCommand(program)
maybeAddInspectFlags(addCypressOpenCommand(program))
.action((...fnArgs) => {
debug('parsed Cypress open %o', fnArgs)
const options = parseVariableOpts(fnArgs, cliArgs)
Expand Down Expand Up @@ -446,7 +456,7 @@ module.exports = {
showVersions(args)
})

addCypressOpenCommand(program)
maybeAddInspectFlags(addCypressOpenCommand(program))
.action((opts) => {
debug('opening Cypress')
require('./exec/open')
Expand All @@ -455,7 +465,7 @@ module.exports = {
.catch(util.logErrorExit1)
})

addCypressRunCommand(program)
maybeAddInspectFlags(addCypressRunCommand(program))
.action((...fnArgs) => {
debug('running Cypress with args %o', fnArgs)
require('./exec/run')
Expand Down
8 changes: 8 additions & 0 deletions cli/lib/exec/open.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ const processOpenOptions = (options = {}) => {
args.push('--global', options.global)
}

if (options.inspect) {
args.push('--inspect')
}

if (options.inspectBrk) {
args.push('--inspectBrk')
}

args.push(...processTestingType(options))

debug('opening from options %j', options)
Expand Down
8 changes: 8 additions & 0 deletions cli/lib/exec/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ const processRunOptions = (options = {}) => {
args.push('--tag', options.tag)
}

if (options.inspect) {
args.push('--inspect')
}

if (options.inspectBrk) {
args.push('--inspectBrk')
}

args.push(...processTestingType(options))

return args
Expand Down
2 changes: 2 additions & 0 deletions cli/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ const parseOpts = (opts) => {
'group',
'headed',
'headless',
'inspect',
'inspectBrk',
'key',
'path',
'parallel',
Expand Down
5 changes: 3 additions & 2 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2937,10 +2937,11 @@ declare namespace Cypress {
*/
type CoreConfigOptions = Partial<Omit<ResolvedConfigOptions, TestingType>>

type DevServerFn<ComponentDevServerOpts = any> = (cypressConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends CoreConfigOptions {
// TODO(tim): Keeping optional until we land the implementation
devServer?: (cypressConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
devServerConfig?: ComponentDevServerOpts
devServer?: Promise<{ devServer: DevServerFn<ResolvedDevServerConfig>}> | { devServer: DevServerFn<ResolvedDevServerConfig> } | DevServerFn<ResolvedDevServerConfig>
devServerConfig?: ComponentDevServerOpts | Promise<ComponentDevServerOpts>
}

/**
Expand Down
80 changes: 80 additions & 0 deletions guides/app-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
## App Lifecycle

This documents the lifecycle of the application, specifically related to managing the current project,
and the various states & inputs that can feed into state changes, and how they are managed

1. Application starts via `cypress open | run --flags`
1. The input is run through `cli/lib/cli.js` for normalization
1. The normalized input is passed into the server, eventually getting to `server/lib/modes/index.ts`
1. The `DataContext` class receives the testing mode (`run` | `open`), and the `modeOptions` (CLI Flags)
1. We call `ctx.initialize`, which based on the `mode` returns a promise for series of steps needed
1. The `DataContext` should act as the global source of truth for all state in the application. It should be passed along where possible. In the `server` package, we can import/use `getCtx` so we don't need to pass it down the chain.
1. The CLI flags & environment variables are used set the initial state of the `coreData`
1. TODO: rename to `appState`?
1. In `open` mode, if the `--global` flag is passed, we start in "global" mode, which allows us to select multiple projects
1. Once a project is selected, either via the CLI being run within a project, or via the `--project` flag, we launch into project mode

## Project Lifecycle

1. Once a project is selected, we source the config from `cypress.config.js`, or wherever the config is specified via the `--configFile` CLI flag:
1. Read the `globalBrowsers`
1. Execute the `configFile` in a child process & reply back with the config, and the require.cache files in the child process
1. If there is an error sourcing the config file, we set an error on the `currentProject` in the root state
1. We source `cypress.env.json` and validate (if it exists)

## **Config Precedence:**

1. Runtime, inline: `it('should do the thing', { retries: { run: 3 } }`
2. `port` from spawned server
3. Returned from `setupNodeEvents` (as these get the options from the CLI)
4. Sourced from CLI
5. Sourced from `cypress.env.json`
6. Sourced from `cypress.config.{js|ts}`
7. Default config options

## **Merging**

Config options are deeply merged:

```bash
# CLI:
cypress run --env FOO=bar

# cypress.config.js
env: {
FOO: 'test'
},
e2e: {
setupNodeEvents (on, config) {
return require('@cypress/code-coverage')(on, config)
},
env: {
e2eRunner: true
}
}

# Would Result in

{
env: { FOO: 'bar', e2eRunner: true }
}
```
## Steps of Sourcing / Execution
1. **Application Start**
1. CLI args & environment are parsed into an "options" object, which is passed along to create the initial application config
2. Browsers are sourced from the machine at startup
3. CLI options `--config baseUrl=http://example.com`, `--env` are gathered for merging later
1. [https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78](https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78)
2. **Project Initialization**
1. When we have a "projectRoot", we execute the `cypress.config.{js|ts}`, and read the `cypress.env.json` - this will be persisted on the state object, so we can compare the diff as we detect/watch changes to these files
1. The child process will also send back a list of files that have been sourced so we can watch them for changes to re-execute the config. *We may want to warn against importing things top-level, so as to minimize the work done in child-process blocking the config*
2. We also pull the "saved state" for the user from the FS App data
1. We only do this in "open mode"
3. At this point, we do a first-pass at creating a known config shape, merging the info together into a single object, picking out the "allowed" list of properties to pass to the `setupNodeEvents`
3. **setupNodeEvents**
1. Once we have selected a `testingType`, we execute the `setupNodeEvents`, passing an "allowed" list of options as the second argument to the function. At this point, we have merged in any CLI options, env vars,
1. If they return a new options object, we merge it with the one we passed in
4. **config → FullConfig**
1. At this point we have the entire config, and we can set the `resolved` property which includes the origin of where the config property was resolved from
4 changes: 3 additions & 1 deletion npm/angular/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export default defineConfig({
'component': {
'componentFolder': 'src/app',
'testFiles': '**/*cy-spec.ts',
'setupNodeEvents': require('./cypress/plugins'),
setupNodeEvents (on, config) {
return require('./cypress/plugins')(on, config)
},
devServer (cypressConfig) {
const { startDevServer } = require('@cypress/webpack-dev-server')
const webpackConfig = require('./cypress/plugins/webpack.config')
Expand Down
16 changes: 13 additions & 3 deletions npm/react/plugins/utils/get-transpile-folders.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ function getTranspileFolders (config) {
const rawFolders = config.addTranspiledFolders ?? []
const folders = rawFolders.map((folder) => path.resolve(config.projectRoot, folder))

// ensure path is absolute
// this is going away soon when we drop component and integration folder
const ensureAbs = (folder) => {
if (!path.isAbsolute(folder)) {
return path.resolve(folder)
}

return folder
}

// user can disable folders, so check first
if (config.componentFolder) {
folders.push(config.componentFolder)
folders.push(ensureAbs(config.componentFolder))
}

if (config.fixturesFolder) {
folders.push(config.fixturesFolder)
folders.push(ensureAbs(config.fixturesFolder))
}

if (config.supportFolder) {
folders.push(config.supportFolder)
folders.push(ensureAbs(config.supportFolder))
}

return folders
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"binary-release": "node ./scripts/binary.js release",
"binary-upload": "node ./scripts/binary.js upload",
"binary-zip": "node ./scripts/binary.js zip",
"build": "lerna run build --stream --no-bail --ignore create-cypress-tests && lerna run build --stream --scope create-cypress-tests",
"build": "lerna run build --stream --no-bail --ignore create-cypress-tests --ignore \"'@packages/{runner}'\" && lerna run build --stream --scope create-cypress-tests",
"build-prod": "lerna run build-prod-ui --stream && lerna run build-prod --stream --ignore create-cypress-tests && lerna run build-prod --stream --scope create-cypress-tests",
"bump": "node ./scripts/binary.js bump",
"check-node-version": "node scripts/check-node-version.js",
Expand Down
4 changes: 2 additions & 2 deletions packages/app/cypress/e2e/integration/code-gen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Code Generation', () => {
cy.findByTestId('file-row').contains('src/stories/Button.cy.js').click()

cy.withCtx(async (ctx) => {
const spec = await (await ctx.project.findSpecs(ctx.currentProject?.projectRoot ?? '', 'component'))
const spec = (await ctx.project.findSpecs(ctx.currentProject ?? '', 'component'))
.find((spec) => spec.relative === 'src/stories/Button.cy.jsx')

expect(spec).to.exist
Expand All @@ -62,7 +62,7 @@ describe('Code Generation', () => {
cy.contains('composeStories')

cy.withCtx(async (ctx) => {
const spec = await (await ctx.project.findSpecs(ctx.currentProject?.projectRoot ?? '', 'component'))
const spec = (await ctx.project.findSpecs(ctx.currentProject ?? '', 'component'))
.find((spec) => spec.relative === 'src/stories/Button.stories.cy.jsx')

expect(spec).to.exist
Expand Down
6 changes: 0 additions & 6 deletions packages/app/cypress/e2e/integration/runs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ describe('App: Runs Page', () => {

it('when no project Id in the config file, shows call to action', () => {
cy.withCtx(async (ctx) => {
if (ctx.currentProject) {
ctx.currentProject.configChildProcess?.process.kill()
ctx.currentProject.config = null
ctx.currentProject.configChildProcess = null
}

await ctx.actions.file.writeFileInProject('cypress.config.js', 'module.exports = {}')
})

Expand Down
19 changes: 12 additions & 7 deletions packages/app/cypress/e2e/integration/sidebar_navigation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
describe('Sidebar Navigation', () => {
before(() => {
beforeEach(() => {
cy.scaffoldProject('todos')
cy.openProject('todos')
cy.startAppServer()
Expand All @@ -18,17 +18,17 @@ describe('Sidebar Navigation', () => {

it('closes the bar when clicking the expand button (if expanded)', () => {
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'true')
cy.findByText('todos').should('be.visible')
cy.findByText('todos').as('title')
cy.get('@title').should('be.visible')
cy.findByLabelText('toggle navigation', {
selector: 'button',
}).click()

cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
cy.findByText('todos').should('not.be.visible')
cy.get('@title').should('not.be.visible')
})

it('has unlabeled menu item that shows the keyboard shortcuts modal (unexpanded state)', () => {
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
cy.get('[data-cy="keyboard-shortcuts"]').should('be.visible')
cy.get('[data-cy="keyboard-shortcuts"]').click()
cy.get('h2').findByText('Keyboard Shortcuts').should('be.visible')
Expand All @@ -43,7 +43,9 @@ describe('Sidebar Navigation', () => {
})

it('shows a tooltip when hovering over menu item', () => {
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
cy.findByLabelText('toggle navigation', {
selector: 'button',
}).click()

cy.get('[data-cy="sidebar-header"').realHover()
cy.contains('#tooltip-target > div', 'todos').should('be.visible')
Expand All @@ -67,7 +69,10 @@ describe('Sidebar Navigation', () => {
})

it('opens the bar when clicking the expand button (if unexpanded)', () => {
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
cy.findByLabelText('toggle navigation', {
selector: 'button',
}).click()

cy.findByText('todos').should('not.be.visible')

cy.findByLabelText('toggle navigation', {
Expand Down Expand Up @@ -142,7 +147,7 @@ describe('Sidebar Navigation', () => {
it('has a menu item labeled "Specs" which takes you to the Spec List page', () => {
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'true')

cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('not.exist')
// cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('not.exist')
cy.findByText('Specs').should('be.visible')
cy.findByText('Specs').click()
cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('be.visible')
Expand Down

3 comments on commit 4626f74

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4626f74 Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/circle-10.0-release-4626f7481c9904fec484aa167a02e0197a3095c4/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4626f74 Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/circle-10.0-release-4626f7481c9904fec484aa167a02e0197a3095c4/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4626f74 Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/circle-10.0-release-4626f7481c9904fec484aa167a02e0197a3095c4/cypress.tgz

Please sign in to comment.