Three Zero Rules
CDAT enforces three “zero” rules. Each eliminates a category of test debt that compounds painfully past 100 tests.
Rule 1: Zero any
Every function signature, every variable, every return type must be properly typed.
// ❌ bad
async getProductData(): Promise<any> {
return await page.evaluate(() => fetch('/api/product').then(r => r.json()));
}
// later in test:
const product = await actions.getProductData();
console.log(product.proce); // typo, no type error, silent prod bug// ✅ good
interface ProductData {
id: string;
name: string;
price: number;
}
async getProductData(): Promise<ProductData> {
return await page.evaluate(() => fetch('/api/product').then(r => r.json()));
}Enforce with ESLint:
{
"rules": {
"@typescript-eslint/no-explicit-any": "warn"
}
}Rule 2: Zero page.waitForTimeout()
Hardcoded timeouts are the #1 source of flake. They fail on slow CI and waste time on fast machines.
// ❌ bad - flaky on CI, slow on dev
await page.waitForTimeout(5000);
await button.click();
// ❌ also bad - same problem, masked
await page.waitForTimeout(2000);
await expect(loader).toBeHidden();// ✅ good - wait for the actual condition
await Cdat.waitAndClick(button);
// ✅ good - explicit state assertion
await Cdat.waitForState(loader, LocatorState.Hidden);The Cdat utility class provides smart-wait helpers that internally combine “wait for state” + “perform action” with proper retry semantics. See /docs/smart-waits for the full API.
Enforce with a custom ESLint rule or grep pre-commit hook:
# fail commit if waitForTimeout appears in features/
git diff --cached --name-only | xargs grep -l 'waitForTimeout' && exit 1 || exit 0Rule 3: Zero else
Use early returns. Nested if-else creates pyramids that obscure the happy path.
// ❌ bad - pyramid of doom
if (user.isValid) {
if (user.hasPermission) {
if (user.balance > 0) {
processOrder(user);
} else {
throw new Error('Insufficient balance');
}
} else {
throw new Error('No permission');
}
} else {
throw new Error('Invalid user');
}// ✅ good - flat, every precondition explicit
if (!user.isValid) throw new Error('Invalid user');
if (!user.hasPermission) throw new Error('No permission');
if (user.balance <= 0) throw new Error('Insufficient balance');
processOrder(user);Putting it together
A CDAT-conformant Action:
async submitOrder(order: OrderData): Promise<OrderResult> {
if (!order.items.length) throw new Error('Empty cart');
await Cdat.waitAndFill(this.components.shippingName, order.name);
await Cdat.waitAndFill(this.components.shippingAddress, order.address);
await Cdat.waitAndClick(this.components.placeOrderButton);
return Cdat.waitForText(this.components.orderConfirmation);
}No any, no waitForTimeout, no else, every dependency explicit. This is the bar.
Next steps
- /docs/smart-waits - full Cdat utility API reference
- /docs/anti-patterns - common mistakes catching teams