跳到正文
cccskills
免费在 GitHub 公开

tdd

Use when implementing any new feature or fixing any bug in this project — both Rust (src-tauri) and TypeScript/Svelte 5 (src). Triggers on phrases like "add X", "fix Y", "implement Z", "change how W works".

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar星标 946(GitHub 946 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md7.0 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Test-Driven Development

Write a failing test first. Then write only enough code to make it pass. No exceptions.

The Iron Law

NO IMPLEMENTATION BEFORE A FAILING TEST.

Write code before a test? Delete it. Start over.

No exceptions:

  • Not for "trivial" functions
  • Not for "obvious" logic
  • Not for bug fixes ("I just need to change one line")
  • Don't keep it as "reference" while writing tests
  • Delete means delete

RED-GREEN-REFACTOR

RED   → Write test → run → watch it FAIL (required)
GREEN → Write minimal code → run → watch it PASS
REFACTOR → Clean up → run → still passes

Never skip RED. A test that passes without you writing any implementation is not a TDD test.

Verification Loops: Inner Loop vs. Outer Loop

To maintain maximum velocity during TDD without context bloat or verification taxes:

  • Inner Loop (Iterative Red-Green-Refactor):
    • Run targeted single-test or single-file test runners (cargo test <test_name> or pnpm test).
    • Or run domain-scoped checks:
      • Frontend changes: pnpm check:ci:frontend
      • Rust changes: pnpm check:ci:rust
      • Changed files only: pnpm check:ci:changed
    • Never run the monolithic verification matrix on every minor edit.
  • Outer Loop (Task Conclusion / Hand-off / Pre-Push):
    • Run the full verification matrix (pnpm check:ci) only after tests pass and implementation is complete, or when modifying cross-domain bindings, IPC contracts, or migrations.

Rust (src-tauri)

Test location: inline #[cfg(test)] module at the bottom of the same .rs file.

Commands:

# Run all tests
cd asyar-launcher/src-tauri && cargo test

# Run tests for one file/module
cargo test <module_name>

# See output from passing tests too
cargo test -- --nocapture

Pattern:

pub fn some_function(input: &str) -> Result<String, AppError> {
    // ← implement AFTER tests are written and failing
    todo!()
}

#[cfg(test)]
mod tests {
    use super::*;

    // Step 1: Write this first, run cargo test, watch it fail
    #[test]
    fn test_some_function_happy_path() {
        let result = some_function("valid input").unwrap();
        assert_eq!(result, "expected output");
    }

    #[test]
    fn test_some_function_error_case() {
        let result = some_function("");
        assert!(result.is_err());
    }

    // Use factory helpers for complex types
    fn make_test_manifest(id: &str) -> ExtensionManifest {
        ExtensionManifest { id: id.to_string(), ..Default::default() }
    }
}

TypeScript / Svelte 5 (src)

Test location: colocated *.test.ts file (e.g. src/services/foo/FooService.test.ts).

Commands:

cd asyar-launcher

# Watch mode during development
pnpm test

# Single run (CI / verify before done)
pnpm test:run

Pattern:

// Step 1: Write this file first, run pnpm test:run, watch it fail
import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock dependencies BEFORE importing the module under test
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }));
vi.mock('../log/logService', () => ({
  logService: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));

// Import AFTER mocks are declared
import { FooService } from './FooService';
import { invoke } from '@tauri-apps/api/core';

describe('FooService', () => {
  beforeEach(() => vi.clearAllMocks());

  it('calls invoke with correct command', async () => {
    vi.mocked(invoke).mockResolvedValueOnce({ id: '1', name: 'Test' });

    const result = await FooService.getItem('1');

    expect(invoke).toHaveBeenCalledWith('get_item', { id: '1' });
    expect(result.name).toBe('Test');
  });

  it('throws on invoke error', async () => {
    vi.mocked(invoke).mockRejectedValueOnce(new Error('not found'));
    await expect(FooService.getItem('missing')).rejects.toThrow('not found');
  });
});

Cross-Module & Registry Integration Tests

Unit tests with heavy mocking often hide multi-module bugs (e.g. duplicate action IDs, conflicting registrations across ActionService and ExtensionLoader).

When a feature interacts across registration boundaries:

  1. Write an integration test that boots the real modules together (e.g. a built-in feature registering its view actions into ActionService).
  2. Assert that no duplicate IDs, duplicate shortcuts, or clashing UI labels are produced in the shared registry.

Quick Reference

TaskCommandLocation
Run Rust testscargo test (in src-tauri/)*.rs inline #[cfg(test)]
Run launcher TS tests (watch)pnpm test (in asyar-launcher/)src/**/*.test.ts
Run launcher TS tests (once)pnpm test:run (in asyar-launcher/)src/**/*.test.ts
Run SDK tests (once)pnpm test:run (in asyar-sdk/)src/**/*.test.ts
Mock Tauri invokevi.mock('@tauri-apps/api/core', ...)top of test file
Mock globals/envvi.stubGlobal() / vi.stubEnv()inside test, restore in afterEach
Inner loop frontend checkspnpm check:ci:frontendrepo root
Inner loop Rust checkspnpm check:ci:rustrepo root
Inner loop git-changed checkspnpm check:ci:changedrepo root
Outer loop full verificationpnpm check:cirepo root

Common Mistakes

MistakeFix
Writing implementation firstDelete it. Write test first.
Test passes without implementationYou wrote the wrong test. It must fail first.
Forgetting vi.mock() hoistingDeclare vi.mock() before any imports
Not clearing mocksAdd beforeEach(() => vi.clearAllMocks())
Testing in Svelte component directlyExtract logic to .ts file, test that
Not running cargo test after Rust changesAlways verify: cd src-tauri && cargo test

Red Flags — STOP and Start Over

  • "I'll write the test after, it's a small change"
  • "This is just a refactor, tests aren't needed"
  • "The bug fix is obvious, I don't need a test"
  • "I already know it works"
  • "Let me just implement it and then add the test"

All of these mean: write the test first. No exceptions.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use before planning or implementing ANY feature, bug fix, refactor, or API change that touches Asyar's architecture — extension system, service layer, IPC contracts, Tauri commands, manifest contributions, or cross-layer data flow. Triggers on new features, new extension APIs, new services, bug fixes involving multiple layers, refactors of infrastructure code, adding new contribution points, and any work that could affect how built-in features or third-party extensions interact with the platform. Also triggers on any task where you need to decide which architectural layer owns the logic, whether Tier 2 extensions can use a capability, or whether a change requires a manifest/permission update. Does NOT trigger for pure CSS/styling fixes (use design-language), isolated unit tests, documentation updates, version bumps, mechanical renames, or answering informational questions about the codebase.

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar9462026年10月27日 更新

Use when building, modifying, or fixing any frontend UI in the Asyar launcher — new components, new views, layout changes, styling decisions, new built-in features, visual bug fixes, and anything in settings or onboarding. Answers "what do I use here?" for fonts, colour, typography, spacing, motion, components, and layout.

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar9462026年10月27日 更新

Reference for the Asyar monorepo structure, SDK workspace linking, lockfile discipline, CI workflows, and release flow. Use this skill whenever working on anything related to pnpm workspace setup, the asyar-sdk dependency, lockfile errors, SDK or launcher version bumps, CI configuration, release workflow, or questions about how local dev differs from CI. Also triggers for "how does the workspace work", "how do I update the SDK version", "where do I run pnpm install", or any confusion about the monorepo layout.

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar9462026年10月27日 更新

Never hand-edit or hand-copy generated content. Use when touching permissions.rs, error.rs, AppError/SearchError variants, runtimes/catalog.fallback.json, any `#[derive(specta::Type)]` struct, any file with an AUTO-GENERATED banner (kinds.ts, gatedPermissions.ts, knownRuntimes.ts, bindings.ts, emoji data), or whenever you are about to write a TypeScript list/type that mirrors a Rust source.

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar9462026年10月27日 更新

Audit IPC message contracts between extensions and the Asyar host. Use when adding a new SDK service, adding a new proxy method, changing a postMessage type string, or reviewing permission gate coverage.

中文简介正在准备中。当前显示原文说明。

Xoshbin/asyar9462026年10月27日 更新

查看 Xoshbin 的全部技能

报告此技能的问题