Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions docs/development-guide/08-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,24 +199,26 @@ These environment variables are used by Cloud Manager's UI tests to override reg
| `CY_TEST_REGION` | ID of region to test (as used by Linode APIv4). | `us-east` | Unset; regions are selected at random |

###### Run Splitting
These environment variables facilitate splitting the Cypress run between multiple runners without the use of any third party services. This can be useful for improving Cypress test performance in some circumstances.
These environment variables facilitate splitting the Cypress run between multiple runners without the use of any third party services. This can be useful for improving Cypress test performance in some circumstances. For additional performance gains, an optional test weights file can be specified using `CY_TEST_SPLIT_RUN_WEIGHTS` (see `CY_TEST_GENWEIGHTS` to generate test weights).

| Environment Variable | Description | Example | Default |
|---------------------------|--------------------------------------------|----------------|----------------------------|
| `CY_TEST_SPLIT_RUN` | Enable run splitting | `1` | Unset; disabled by default |
| `CY_TEST_SPLIT_RUN_INDEX` | Numeric index for each Cypress test runner | `1`, `2`, etc. | Unset |
| `CY_TEST_SPLIT_RUN_TOTAL` | Total number of runners for the tests | `2` | Unset |
| Environment Variable | Description | Example | Default |
|-----------------------------|--------------------------------------------|------------------|----------------------------|
| `CY_TEST_SPLIT_RUN` | Enable run splitting | `1` | Unset; disabled by default |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should these be added to our .env.example? What are the defaults you've found to be a good balance for these?

@jdamore-linode jdamore-linode Jul 30, 2024 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hm, I don't think these belong there since they're more intended for CI and not super practical to use locally (you could, but the cost of having multiple Cypress and Chrome instances running at once plus the hassle of setting up separate test accounts for each runner probably makes it more trouble than it's worth).

However, I've never really considered documenting any of these in .env.example, so I'll take a quick look later and add any that might make sense! Thanks Jaalah!

Edit: It also just occurred to me that I've always just assumed our .env.example file is specifically for documenting variables for local usage/development, but if we consider it to be a complete list of all the env vars that might be relevant to Cloud then I'm definitely in favor of adding all this there

| `CY_TEST_SPLIT_RUN_INDEX` | Numeric index for each Cypress test runner | `1`, `2`, etc. | Unset |
| `CY_TEST_SPLIT_RUN_TOTAL` | Total number of runners for the tests | `2` | Unset |
| `CY_TEST_SPLIT_RUN_WEIGHTS` | Path to test weights file | `./weights.json` | Unset; disabled by default |

###### Development, Logging, and Reporting
Environment variables related to Cypress logging and reporting, as well as report generation.

| Environment Variable | Description | Example | Default |
|---------------------------------|-----------------------------------------------|-----------|----------------------------|
| `CY_TEST_USER_REPORT` | Log test account information when tests begin | `1` | Unset; disabled by default |
| `CY_TEST_JUNIT_REPORT` | Enable JUnit reporting | `1` | Unset; disabled by default |
| `CY_TEST_DISABLE_FILE_WATCHING` | Disable file watching in Cypress UI | `1` | Unset; disabled by default |
| `CY_TEST_DISABLE_RETRIES` | Disable test retries on failure in CI | `1` | Unset; disabled by default |
| `CY_TEST_FAIL_ON_MANAGED` | Fail affected tests when Managed is enabled | `1` | Unset; disabled by default |
| Environment Variable | Description | Example | Default |
|---------------------------------|----------------------------------------------------|------------------|----------------------------|
| `CY_TEST_USER_REPORT` | Log test account information when tests begin | `1` | Unset; disabled by default |
| `CY_TEST_JUNIT_REPORT` | Enable JUnit reporting | `1` | Unset; disabled by default |
| `CY_TEST_DISABLE_FILE_WATCHING` | Disable file watching in Cypress UI | `1` | Unset; disabled by default |
| `CY_TEST_DISABLE_RETRIES` | Disable test retries on failure in CI | `1` | Unset; disabled by default |
| `CY_TEST_FAIL_ON_MANAGED` | Fail affected tests when Managed is enabled | `1` | Unset; disabled by default |
| `CY_TEST_GENWEIGHTS` | Generate and output test weights to the given path | `./weights.json` | Unset; disabled by default |

### Writing End-to-End Tests

Expand Down
2 changes: 2 additions & 0 deletions packages/manager/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { fetchAccount } from './cypress/support/plugins/fetch-account';
import { fetchLinodeRegions } from './cypress/support/plugins/fetch-linode-regions';
import { splitCypressRun } from './cypress/support/plugins/split-run';
import { enableJunitReport } from './cypress/support/plugins/junit-report';
import { generateTestWeights } from './cypress/support/plugins/generate-weights';
import { logTestTagInfo } from './cypress/support/plugins/test-tagging-info';

/**
Expand Down Expand Up @@ -70,6 +71,7 @@ export default defineConfig({
logTestTagInfo,
splitCypressRun,
enableJunitReport,
generateTestWeights,
]);
},
},
Expand Down
155 changes: 155 additions & 0 deletions packages/manager/cypress/support/plugins/generate-weights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import type { CypressPlugin } from './plugin';
import { DateTime } from 'luxon';
import { writeFileSync } from 'fs';
import { resolve } from 'path';
import { object, string, array, number, SchemaOf } from 'yup';

// The name of the environment variable to read to check if generation is enabled.
// The value should be a path to the weights file.
const envVarName = 'CY_TEST_GENWEIGHTS';

/**
* Describes spec file weights for a test suite.
*/
export interface SpecWeights {
/**
* Spec weight metadata.
*/
meta: {
/**
* Date and time that test spec weights were generated.
*/
datetime: string;

/**
* Total test weight.
*/
totalWeight: number;

/**
* Total test run duration in milliseconds.
*/
totalDuration: number;
};
/**
* Array of spec weights.
*/
weights: SpecWeight[];
}

/**
* Describes the weight of an individual spec file.
*/
export interface SpecWeight extends SpecResult {
/**
* Spec weight.
*/
weight: number;
}

/**
* Spec weights schema for JSON parsing, etc.
*/
export const specWeightsSchema: SchemaOf<SpecWeights> = object({
meta: object({
datetime: string().required(),
totalWeight: number().required(),
totalDuration: number().required(),
}).required(),
weights: array(
object({
filepath: string().required(),
duration: number().required(),
weight: number().required(),
})
).required(),
});

/**
* Describes the duration of an individual spec file.
*
* Used in the process of calculating weights for each spec.
*/
interface SpecResult {
/**
* Relative path to spec file.
*/
filepath: string;

/**
* Spec run duration in milliseconds.
*/
duration: number;
}

/**
* Enables test weight generation when `CY_TEST_GENWEIGHTS` is defined.
*
* @returns Cypress configuration object.
*/
export const generateTestWeights: CypressPlugin = (on, config) => {
const specResults: SpecResult[] = [];

if (!!config.env[envVarName]) {
const writeFilepath = config.env[envVarName];

// Capture duration after each spec runs.
on('after:spec', (spec, results) => {
const duration = results.stats.duration;
if (duration) {
specResults.push({
filepath: spec.relative,
duration,
});
} else {
console.warn(
`Failed to record test information for '${spec.relative}'`
);
}
});

// Aggregate spec durations and save as a spec weights JSON file.
on(
'after:run',
(
results:
| CypressCommandLine.CypressRunResult
| CypressCommandLine.CypressFailedRunResult
) => {
// Determine whether this is a failed run. "Failed" in this context means
// that Cypress itself failed to run, not that the test results contained failures.
const isFailedResult = (
results:
| CypressCommandLine.CypressRunResult
| CypressCommandLine.CypressFailedRunResult
): results is CypressCommandLine.CypressFailedRunResult => {
return 'failures' in results;
};

if (!isFailedResult(results)) {
const totalWeight = 100;
const totalDuration = results.totalDuration;
const weights: SpecWeights = {
meta: {
datetime: DateTime.now().toISO(),
totalWeight,
totalDuration,
},
weights: specResults.map(
(specResult: SpecResult): SpecWeight => {
return {
filepath: specResult.filepath,
duration: specResult.duration,
weight: (specResult.duration / totalDuration) * totalWeight,
};
}
),
};

const resolvePath = resolve(writeFilepath);
writeFileSync(resolvePath, JSON.stringify(weights), 'utf-8');
}
}
);
}
};
Loading