This directory contains end-to-end tests for the Ctrlplane platform. The tests use Playwright and are organized by domain area and functionality.
The tests are categorized into:
- API Tests: Located in the
tests/api/directory, testing backend API functionality - UI Tests: Located directly in the
tests/directory, testing frontend UI functionality
API tests are further organized by resource type:
tests/api/environments.spec.ts- Environment-related API endpointstests/api/resource-selectors.spec.ts- Resource selector functionalitytests/api/resources.spec.ts- Resource management endpointstests/api/yaml-import.spec.ts- YAML entity import functionalitytests/api/random-prefix-yaml.spec.ts- YAML import with random prefixestests/api/template-yaml.spec.ts- YAML import with template processingtests/api/policies/- Policy-related API endpointstests/api/policies/release-targets.spec.ts- Policy release target functionalitytests/api/policies/policies.spec.ts- Core policy functionality
tests/systems.spec.ts- System management via UI and API
Helper functions for test setup are located in:
api/utils.ts- Contains modular utility functions for creating test resourcesapi/yaml-loader.ts- Utilities for importing test entities from YAML filestests/fixtures.ts- Defines test fixtures for workspace and API accesstests/fixtures/*.yaml- YAML fixture files for test data
The YAML Entity Loader allows you to define entire test environments in YAML files and import them with a single function call. This makes it easy to create complex test scenarios with multiple related entities.
Example usage:
import {
cleanupImportedEntities,
importEntitiesFromYaml,
} from "../../api/yaml-loader";
// In a beforeAll hook - basic import
const yamlPath = path.join(
process.cwd(),
"tests",
"fixtures",
"test-system.yaml",
);
const entities = await importEntitiesFromYaml(
api,
workspace.id,
yamlPath,
tracer,
);
// Or with a random prefix to avoid naming conflicts
const prefixedEntities = await importEntitiesFromYaml(
api,
workspace.id,
yamlPath,
{
addRandomPrefix: true, // Add a random prefix to all entities
updateSelectors: true, // Update resource selectors to use the prefix
},
);
// With a custom prefix
const customEntities = await importEntitiesFromYaml(
api,
workspace.id,
yamlPath,
{
customPrefix: "my-unique-prefix",
updateSelectors: true,
},
);
// In tests, use the imported entities
test("test something", async ({ api }) => {
const systemId = entities.system.id;
// ... test using the imported entities
});
// In afterAll hook, clean up
await cleanupImportedEntities(api, entities);The YAML entity loader supports adding random or custom prefixes to avoid naming conflicts. This is particularly useful when:
- Running tests in parallel
- Running the same test multiple times
- Working in a shared test environment
Options include:
addRandomPrefix: true- Automatically adds a timestamp + random string prefixcustomPrefix: "my-prefix"- Uses your own custom prefixupdateSelectors: true- Updates resource selectors to work with the prefixed entity names
When using prefixes, the imported entities contain both the prefixed names and the original names:
// Example of a prefixed entity
{
prefix: "test-1682542981234-a1b2c3",
system: {
id: "123-456",
name: "test-1682542981234-a1b2c3-yaml-test-system",
slug: "test-1682542981234-a1b2c3-yaml-test-system",
originalName: "yaml-test-system"
},
// ...other entities with similar structure
}The YAML entity loader supports a template engine that lets you use dynamic values in your YAML files. This is useful for:
- Creating unique identifiers for each test run
- Generating random test data
- Making YAML fixtures more reusable
Templates use Mustache syntax with double curly braces: {{ functionName() }}
Example YAML with templates:
system:
name: "test-system-{{ runid() }}"
slug: "test-{{ slug('System ' + timestamp()) }}"
resources:
- name: "api-server-{{ randomString(5) }}"
identifier: "{{ runid('resource') }}-api"
version: "v1"
config:
replicas: { { random(1, 5) } }Available template functions:
{{ runid([prefix]) }}- Generate a unique run ID (timestamp + random number){{ uuid() }}- Generate a UUID v4{{ timestamp() }}- Get current timestamp{{ random(min, max) }}- Generate a random number between min and max{{ randomString(length) }}- Generate a random alphanumeric string{{ randomName([prefix]) }}- Generate a random name{{ slug(text) }}- Convert text to a slug (lowercase, hyphenated)
To use templates, set the processTemplates option to true (enabled by default):
const entities = await importEntitiesFromYaml(api, workspace.id, yamlPath, {
processTemplates: true, // Default is true
});You can also define custom template helpers:
const entities = await importEntitiesFromYaml(api, workspace.id, yamlPath, {
templateHelpers: {
environment: () => process.env.NODE_ENV || "test",
customPrefix: () => "my-prefix",
},
});YAML files should be structured as follows:
system:
name: test-system
slug: test-system
description: Test system
environments:
- name: Production
description: Production environment
metadata:
env: prod
resources:
- name: Resource 1
kind: TestResource
identifier: test-resource-1
version: v1
config:
key: value
metadata:
env: prod
deployments:
- name: API Deployment
slug: api-deployment
description: API deployment
policies:
- name: Production Policy
targets:
- environmentSelector:
type: metadata
operator: equals
key: env
value: prodTo run all tests:
pnpm testTo run API tests:
pnpm test:apiTo run the YAML import tests:
pnpm test:yaml # Basic YAML import
pnpm test:yaml-prefixed # YAML import with random prefix
pnpm test:yaml-template # YAML import with template processingTo run a specific test file:
pnpm exec playwright test tests/api/resources.spec.tsTo run in debug mode:
pnpm test:debug- Isolation: Each test should be independent and not rely on state from other tests
- Modularity: Use utility functions to create test resources
- Clear assertions: Each test should have clear expectations
- Descriptive naming: Test names should clearly describe what they're testing
- Specific fixtures: Use YAML files or utility functions to create dedicated test data
Playwright configuration is in playwright.config.ts, which sets up:
- Browser environments
- Authentication setup
- Parallelization
- Web server management