|
| 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 |
0 commit comments