Skip to content

3 min read

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 0

Rule 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