Skip to content

Commit 4626f74

Browse files
tgriesserlmiller1990estrada9166JessicaSachs
authored
feat: ProjectLifecycleManager & general launchpad cleanup (#19347)
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>
1 parent 67c42fc commit 4626f74

File tree

217 files changed

+4335
-4601
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

217 files changed

+4335
-4601
lines changed

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@
3737

3838
// Volar is the main extension that powers Vue's language features.
3939
"volar.autoCompleteRefs": false,
40-
"volar.takeOverMode.enabled": true
40+
// "volar.takeOverMode.enabled": true
4141
}

circle.yml

+6-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ macWorkflowFilters: &mac-workflow-filters
3939
or:
4040
- equal: [ develop, << pipeline.git.branch >> ]
4141
- equal: [ '10.0-release', << pipeline.git.branch >> ]
42+
- equal: [ 'tgriesser/10.0-release/refactor-lifecycle-ui', << pipeline.git.branch >> ]
4243
- equal: [ renovate/cypress-request-2.x, << pipeline.git.branch >> ]
4344
- matches:
4445
pattern: "-release$"
@@ -49,6 +50,7 @@ windowsWorkflowFilters: &windows-workflow-filters
4950
or:
5051
- equal: [ develop, << pipeline.git.branch >> ]
5152
- equal: [ '10.0-release', << pipeline.git.branch >> ]
53+
- equal: [ 'tgriesser/10.0-release/refactor-lifecycle-ui', << pipeline.git.branch >> ]
5254
- equal: [ test-binary-downstream-windows, << pipeline.git.branch >> ]
5355
- matches:
5456
pattern: "-release$"
@@ -618,7 +620,6 @@ commands:
618620
command: |
619621
git fetch origin pull/<<parameters.pull_request_id>>/head:pr-<<parameters.pull_request_id>>
620622
git checkout pr-<<parameters.pull_request_id>>
621-
git log -n 2
622623
623624
test-binary-against-rwa:
624625
description: |
@@ -1334,7 +1335,7 @@ jobs:
13341335

13351336
run-launchpad-integration-tests-chrome:
13361337
<<: *defaults
1337-
parallelism: 1
1338+
parallelism: 2
13381339
steps:
13391340
- run-new-ui-tests:
13401341
browser: chrome
@@ -1344,7 +1345,7 @@ jobs:
13441345

13451346
run-app-component-tests-chrome:
13461347
<<: *defaults
1347-
parallelism: 1
1348+
parallelism: 7
13481349
steps:
13491350
- run-new-ui-tests:
13501351
browser: chrome
@@ -1354,7 +1355,7 @@ jobs:
13541355

13551356
run-app-integration-tests-chrome:
13561357
<<: *defaults
1357-
parallelism: 1
1358+
parallelism: 2
13581359
steps:
13591360
- run-new-ui-tests:
13601361
browser: chrome
@@ -1668,7 +1669,7 @@ jobs:
16681669
- run:
16691670
name: Check current branch to persist artifacts
16701671
command: |
1671-
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
1672+
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
16721673
echo "Not uploading artifacts or posting install comment for this branch."
16731674
circleci-agent step halt
16741675
fi

cli/lib/cli.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@ const addCypressOpenCommand = (program) => {
288288
.option('--dev', text('dev'), coerceFalse)
289289
}
290290

291+
const maybeAddInspectFlags = (program) => {
292+
if (process.argv.includes('--dev')) {
293+
return program
294+
.option('--inspect', 'Node option')
295+
.option('--inspect-brk', 'Node option')
296+
}
297+
298+
return program
299+
}
300+
291301
/**
292302
* Casts known command line options for "cypress run" to their intended type.
293303
* For example if the user passes "--port 5005" the ".port" property should be
@@ -336,7 +346,7 @@ module.exports = {
336346
debug('creating program parser')
337347
const program = createProgram()
338348

339-
addCypressRunCommand(program)
349+
maybeAddInspectFlags(addCypressRunCommand(program))
340350
.action((...fnArgs) => {
341351
debug('parsed Cypress run %o', fnArgs)
342352
const options = parseVariableOpts(fnArgs, cliArgs)
@@ -377,7 +387,7 @@ module.exports = {
377387
debug('creating program parser')
378388
const program = createProgram()
379389

380-
addCypressOpenCommand(program)
390+
maybeAddInspectFlags(addCypressOpenCommand(program))
381391
.action((...fnArgs) => {
382392
debug('parsed Cypress open %o', fnArgs)
383393
const options = parseVariableOpts(fnArgs, cliArgs)
@@ -446,7 +456,7 @@ module.exports = {
446456
showVersions(args)
447457
})
448458

449-
addCypressOpenCommand(program)
459+
maybeAddInspectFlags(addCypressOpenCommand(program))
450460
.action((opts) => {
451461
debug('opening Cypress')
452462
require('./exec/open')
@@ -455,7 +465,7 @@ module.exports = {
455465
.catch(util.logErrorExit1)
456466
})
457467

458-
addCypressRunCommand(program)
468+
maybeAddInspectFlags(addCypressRunCommand(program))
459469
.action((...fnArgs) => {
460470
debug('running Cypress with args %o', fnArgs)
461471
require('./exec/run')

cli/lib/exec/open.js

+8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ const processOpenOptions = (options = {}) => {
4848
args.push('--global', options.global)
4949
}
5050

51+
if (options.inspect) {
52+
args.push('--inspect')
53+
}
54+
55+
if (options.inspectBrk) {
56+
args.push('--inspectBrk')
57+
}
58+
5159
args.push(...processTestingType(options))
5260

5361
debug('opening from options %j', options)

cli/lib/exec/run.js

+8
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ const processRunOptions = (options = {}) => {
137137
args.push('--tag', options.tag)
138138
}
139139

140+
if (options.inspect) {
141+
args.push('--inspect')
142+
}
143+
144+
if (options.inspectBrk) {
145+
args.push('--inspectBrk')
146+
}
147+
140148
args.push(...processTestingType(options))
141149

142150
return args

cli/lib/util.js

+2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ const parseOpts = (opts) => {
212212
'group',
213213
'headed',
214214
'headless',
215+
'inspect',
216+
'inspectBrk',
215217
'key',
216218
'path',
217219
'parallel',

cli/types/cypress.d.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -2937,10 +2937,11 @@ declare namespace Cypress {
29372937
*/
29382938
type CoreConfigOptions = Partial<Omit<ResolvedConfigOptions, TestingType>>
29392939

2940+
type DevServerFn<ComponentDevServerOpts = any> = (cypressConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
29402941
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends CoreConfigOptions {
29412942
// TODO(tim): Keeping optional until we land the implementation
2942-
devServer?: (cypressConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
2943-
devServerConfig?: ComponentDevServerOpts
2943+
devServer?: Promise<{ devServer: DevServerFn<ResolvedDevServerConfig>}> | { devServer: DevServerFn<ResolvedDevServerConfig> } | DevServerFn<ResolvedDevServerConfig>
2944+
devServerConfig?: ComponentDevServerOpts | Promise<ComponentDevServerOpts>
29442945
}
29452946

29462947
/**

guides/app-lifecycle.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## App Lifecycle
2+
3+
This documents the lifecycle of the application, specifically related to managing the current project,
4+
and the various states & inputs that can feed into state changes, and how they are managed
5+
6+
1. Application starts via `cypress open | run --flags`
7+
1. The input is run through `cli/lib/cli.js` for normalization
8+
1. The normalized input is passed into the server, eventually getting to `server/lib/modes/index.ts`
9+
1. The `DataContext` class receives the testing mode (`run` | `open`), and the `modeOptions` (CLI Flags)
10+
1. We call `ctx.initialize`, which based on the `mode` returns a promise for series of steps needed
11+
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.
12+
1. The CLI flags & environment variables are used set the initial state of the `coreData`
13+
1. TODO: rename to `appState`?
14+
1. In `open` mode, if the `--global` flag is passed, we start in "global" mode, which allows us to select multiple projects
15+
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
16+
17+
## Project Lifecycle
18+
19+
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:
20+
1. Read the `globalBrowsers`
21+
1. Execute the `configFile` in a child process & reply back with the config, and the require.cache files in the child process
22+
1. If there is an error sourcing the config file, we set an error on the `currentProject` in the root state
23+
1. We source `cypress.env.json` and validate (if it exists)
24+
25+
## **Config Precedence:**
26+
27+
1. Runtime, inline: `it('should do the thing', { retries: { run: 3 } }`
28+
2. `port` from spawned server
29+
3. Returned from `setupNodeEvents` (as these get the options from the CLI)
30+
4. Sourced from CLI
31+
5. Sourced from `cypress.env.json`
32+
6. Sourced from `cypress.config.{js|ts}`
33+
7. Default config options
34+
35+
## **Merging**
36+
37+
Config options are deeply merged:
38+
39+
```bash
40+
# CLI:
41+
cypress run --env FOO=bar
42+
43+
# cypress.config.js
44+
env: {
45+
FOO: 'test'
46+
},
47+
e2e: {
48+
setupNodeEvents (on, config) {
49+
return require('@cypress/code-coverage')(on, config)
50+
},
51+
env: {
52+
e2eRunner: true
53+
}
54+
}
55+
56+
# Would Result in
57+
58+
{
59+
env: { FOO: 'bar', e2eRunner: true }
60+
}
61+
```
62+
63+
## Steps of Sourcing / Execution
64+
65+
1. **Application Start**
66+
1. CLI args & environment are parsed into an "options" object, which is passed along to create the initial application config
67+
2. Browsers are sourced from the machine at startup
68+
3. CLI options `--config baseUrl=http://example.com`, `--env` are gathered for merging later
69+
1. [https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78](https://gist.github.com/tgriesser/5111edc0e31b9db61755b0bddbf93e78)
70+
2. **Project Initialization**
71+
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
72+
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*
73+
2. We also pull the "saved state" for the user from the FS App data
74+
1. We only do this in "open mode"
75+
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`
76+
3. **setupNodeEvents**
77+
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,
78+
1. If they return a new options object, we merge it with the one we passed in
79+
4. **config → FullConfig**
80+
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

npm/angular/cypress.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ export default defineConfig({
99
'component': {
1010
'componentFolder': 'src/app',
1111
'testFiles': '**/*cy-spec.ts',
12-
'setupNodeEvents': require('./cypress/plugins'),
12+
setupNodeEvents (on, config) {
13+
return require('./cypress/plugins')(on, config)
14+
},
1315
devServer (cypressConfig) {
1416
const { startDevServer } = require('@cypress/webpack-dev-server')
1517
const webpackConfig = require('./cypress/plugins/webpack.config')

npm/react/plugins/utils/get-transpile-folders.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,27 @@ function getTranspileFolders (config) {
55
const rawFolders = config.addTranspiledFolders ?? []
66
const folders = rawFolders.map((folder) => path.resolve(config.projectRoot, folder))
77

8+
// ensure path is absolute
9+
// this is going away soon when we drop component and integration folder
10+
const ensureAbs = (folder) => {
11+
if (!path.isAbsolute(folder)) {
12+
return path.resolve(folder)
13+
}
14+
15+
return folder
16+
}
17+
818
// user can disable folders, so check first
919
if (config.componentFolder) {
10-
folders.push(config.componentFolder)
20+
folders.push(ensureAbs(config.componentFolder))
1121
}
1222

1323
if (config.fixturesFolder) {
14-
folders.push(config.fixturesFolder)
24+
folders.push(ensureAbs(config.fixturesFolder))
1525
}
1626

1727
if (config.supportFolder) {
18-
folders.push(config.supportFolder)
28+
folders.push(ensureAbs(config.supportFolder))
1929
}
2030

2131
return folders

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"binary-release": "node ./scripts/binary.js release",
1313
"binary-upload": "node ./scripts/binary.js upload",
1414
"binary-zip": "node ./scripts/binary.js zip",
15-
"build": "lerna run build --stream --no-bail --ignore create-cypress-tests && lerna run build --stream --scope create-cypress-tests",
15+
"build": "lerna run build --stream --no-bail --ignore create-cypress-tests --ignore \"'@packages/{runner}'\" && lerna run build --stream --scope create-cypress-tests",
1616
"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",
1717
"bump": "node ./scripts/binary.js bump",
1818
"check-node-version": "node scripts/check-node-version.js",

packages/app/cypress/e2e/integration/code-gen.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('Code Generation', () => {
4141
cy.findByTestId('file-row').contains('src/stories/Button.cy.js').click()
4242

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

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

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

6868
expect(spec).to.exist

packages/app/cypress/e2e/integration/runs.spec.ts

-6
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,6 @@ describe('App: Runs Page', () => {
5656

5757
it('when no project Id in the config file, shows call to action', () => {
5858
cy.withCtx(async (ctx) => {
59-
if (ctx.currentProject) {
60-
ctx.currentProject.configChildProcess?.process.kill()
61-
ctx.currentProject.config = null
62-
ctx.currentProject.configChildProcess = null
63-
}
64-
6559
await ctx.actions.file.writeFileInProject('cypress.config.js', 'module.exports = {}')
6660
})
6761

packages/app/cypress/e2e/integration/sidebar_navigation.spec.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
describe('Sidebar Navigation', () => {
2-
before(() => {
2+
beforeEach(() => {
33
cy.scaffoldProject('todos')
44
cy.openProject('todos')
55
cy.startAppServer()
@@ -18,17 +18,17 @@ describe('Sidebar Navigation', () => {
1818

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

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

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

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

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

6971
it('opens the bar when clicking the expand button (if unexpanded)', () => {
70-
cy.get('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
72+
cy.findByLabelText('toggle navigation', {
73+
selector: 'button',
74+
}).click()
75+
7176
cy.findByText('todos').should('not.be.visible')
7277

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

145-
cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('not.exist')
150+
// cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('not.exist')
146151
cy.findByText('Specs').should('be.visible')
147152
cy.findByText('Specs').click()
148153
cy.get('[data-cy="app-header-bar"]').findByText('Specs-Index').should('be.visible')

0 commit comments

Comments
 (0)