> ## Documentation Index
> Fetch the complete documentation index at: https://docs.codemod.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

Test your JSSG codemod using snapshot testing with single-file fixtures or full directory snapshots.

## Test structure

JSSG tests support two fixture layouts:

### Single-file fixtures

Each test case contains one `input.*` file and one `expected.*` file:

```plaintext theme={null}
tests/
  basic-transform/          # Test case directory
    input.ts               # Code before transformation
    expected.ts            # Expected output after transformation
  edge-cases/               # Another test case
    input.tsx
    expected.tsx
```

**File naming:**

* Input files must be named `input.ts` (or appropriate extension)
* Expected files must be named `expected.ts` (or appropriate extension)
* File extensions should match your target language (`.ts`, `.tsx`, `.js`, `.jsx`)

### Directory snapshot fixtures

Use `input/` and `expected/` directories when your codemod creates or deletes files, or when multiple files need to be transformed together:

```plaintext theme={null}
tests/
  multi-file-case/
    input/
      query_type.ts
      legacy_custom_email.ts
    expected/
      query_type.ts
      custom_email_resolver.ts
```

Directory fixtures are compared as snapshots of the whole tree:

* Files present in both `input/` and `expected/` must match after the codemod runs
* Files present only in `expected/` are treated as created-file assertions
* Files present only in `input/` are treated as deleted-file assertions
* Files produced by the codemod but absent from `expected/` fail as unexpected extra files
* Test reporting still stays per file, using names like `<fixture>_<relative-path>`

<Warning>
  The test directory must be the **parent** folder that contains your test case subdirectories (e.g., `./tests`). Passing a specific test case directory like `./tests/basic-transform` will result in a "No test cases found" error. To run a specific test case, use the `--filter` flag instead.
</Warning>

## Command reference

### Basic syntax

```bash theme={null}
npx codemod jssg test -l <language> <codemod-file> [test-directory]
```

<ParamField path="-l, --language" type="string" required>Language parser to use (e.g., "tsx", "javascript", "typescript").</ParamField>
<ParamField path="codemod-file" type="string" required>Path to your transform file (e.g., "./scripts/codemod.ts").</ParamField>
<ParamField path="test-directory" type="string">Path to test directory (defaults to "./tests").</ParamField>

### Common options

```bash theme={null}
# Update expected outputs when changes are intentional
npx codemod jssg test -l tsx ./scripts/codemod.ts -u

# Run only tests matching a pattern (matches reported test names)
npx codemod jssg test -l tsx ./scripts/codemod.ts --filter "edge-cases"

# Show detailed output for debugging
npx codemod jssg test -l tsx ./scripts/codemod.ts -v

# Run tests one at a time (helpful for debugging)
npx codemod jssg test -l tsx ./scripts/codemod.ts --sequential

# Use loose comparison (ignores formatting and property ordering)
npx codemod jssg test -l tsx ./scripts/codemod.ts --strictness loose
```

<Info>
  See all available options in the [CLI reference](/cli#codemod-jssg).
</Info>

## Creating effective test cases

**1. Start simple:**

```ts theme={null}
// input.ts
console.log("test");

// expected.ts  
logger.log("test");
```

**2. Add edge cases:**

```ts theme={null}
// input.ts
// Should not transform
const preserved = "unchanged";

// Should transform
console.log("transform me");
```

**3. Test different file types:**

```tsx theme={null}
// input.tsx
function Component() {
  console.log("Hello");
  return <div>World</div>;
}
```

## Understanding test results

### Exit codes

* **0**: All tests passed
* **1**: At least one test failed (diffs are printed)
* **2**: Error occurred (e.g., syntax error in codemod)

### Reading test output

```bash theme={null}
✓ tests/basic-transform
✗ tests/edge-cases
  Expected: logger.log("test")
  Actual:   console.log("test")
```

<Check>
  **Pro tip:** Use `-v` flag for detailed output when debugging test failures.
</Check>

### Snapshot updates

`--update-snapshots` behaves differently depending on fixture layout:

* Single-file fixtures update the matching `expected.*` file
* Directory fixtures update changed files, create missing expected files, and remove stale expected files that should no longer exist

`metrics.json` snapshots continue to be handled separately from directory tree comparisons.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Language mismatch">
    **Problem:** Tests fail with parsing errors.<br /><br />
    **Solution:** Ensure the `-l` flag matches your source files:

    ```bash theme={null}
    # For TypeScript files
    npx codemod jssg test -l typescript ./scripts/codemod.ts

    # For TSX files  
    npx codemod jssg test -l tsx ./scripts/codemod.ts

    # For JavaScript files
    npx codemod jssg test -l javascript ./scripts/codemod.ts
    ```
  </Accordion>

  <Accordion title="Files not found">
    **Problem:** "No test files found" error.<br /><br />
    **Solution:** Check your directory structure and file names:

    ```bash theme={null}
    # Verify structure
    ls -la tests/basic-transform/
    # Should show: input.ts  expected.ts

    # Check file names are correct
    ls tests/basic-transform/
    # Should show: input.ts  expected.ts
    ```
  </Accordion>

  <Accordion title="No test cases found when pointing to a subdirectory">
    **Problem:** Running `npx codemod jssg test ... ./tests/my-case` gives "No test cases found".<br /><br />
    **Solution:** The test directory argument must be the parent `tests/` folder, not a specific test case subdirectory. Use `--filter` to run a specific case:

    ```bash theme={null}
    npx codemod jssg test -l tsx ./scripts/codemod.ts ./tests --filter "my-case"
    ```
  </Accordion>

  <Accordion title="Transform not applied">
    **Problem:** Test passes but no changes were made.<br /><br />
    **Solution:** This is normal if your transform returns `null` for unchanged files. If you expect changes:

    ```bash theme={null}
    # Run with verbose output to debug
    npx codemod jssg test -l tsx ./scripts/codemod.ts -v
    ```
  </Accordion>

  <Accordion title="Unexpected diffs">
    **Problem:** Test fails with unexpected output.<br /><br />
    **Solution:** Update expected output if changes are intentional:

    ```bash theme={null}
    # Update snapshots
    npx codemod jssg test -l tsx ./scripts/codemod.ts -u
    ```

    For directory fixtures, this can also create new files in `expected/` or remove stale ones.
  </Accordion>

  <Accordion title="Codemod syntax errors">
    **Problem:** "Syntax error in codemod" or TypeScript errors.<br /><br />
    **Solution:** Check your transform file:

    ```bash theme={null}
    # Validate TypeScript
    npx tsc --noEmit ./scripts/codemod.ts

    # Test with a simple file first
    echo 'console.log("test");' > test-input.js
    npx codemod jssg run -l javascript ./scripts/codemod.ts test-input.js
    ```
  </Accordion>
</AccordionGroup>

## Workflow integration

### Package script

Add to your `package.json`:

```json package.json theme={null}
{
  "scripts": {
    "test": "npx codemod jssg test -l tsx ./scripts/codemod.ts",
    "test:update": "npx codemod jssg test -l tsx ./scripts/codemod.ts -u",
    "test:verbose": "npx codemod jssg test -l tsx ./scripts/codemod.ts -v"
  }
}
```

### CI/CD integration

```yaml .github/workflows/test.yml theme={null}
name: Test Codemod
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '24' # Use current LTS
      - run: npm test
```

## Comparison strictness

Use the `--strictness` flag to control how expected and actual outputs are compared. This helps tests pass when outputs are semantically equivalent but differ in formatting.

```bash theme={null}
npx codemod jssg test -l tsx ./scripts/codemod.ts --strictness <LEVEL>
```

### Strictness levels

| Level    | Description                                                                                                  |
| -------- | ------------------------------------------------------------------------------------------------------------ |
| `strict` | (Default) Exact string equality. Fails on any whitespace or formatting differences.                          |
| `cst`    | Compare Concrete Syntax Trees. Includes all tokens but ignores whitespace content. Preserves ordering.       |
| `ast`    | Compare Abstract Syntax Trees. Ignores formatting and whitespace, preserves ordering.                        |
| `loose`  | Loose AST comparison. Ignores formatting and ordering of unordered children (e.g., object members, imports). |

### What `loose` comparison ignores

The `loose` level ignores ordering for semantically unordered constructs:

* **Object property ordering**: `{a: 1, b: 2}` matches `{b: 2, a: 1}`
* **Import ordering**: `import {a, b} from 'x'` matches `import {b, a} from 'x'`
* **Keyword argument ordering** (Python): `func(a=1, b=2)` matches `func(b=2, a=1)`
* **Interface member ordering**: Order of properties in TypeScript interfaces
* **Derive attribute ordering** (Rust): `#[derive(Debug, Clone)]` matches `#[derive(Clone, Debug)]`

### When to use each level

* **`strict`**: Default. Use when exact output matters (most cases).
* **`cst`**: Use when you want to compare token structure but ignore whitespace content.
* **`ast`**: Use when you want to ignore formatting differences but preserve ordering.
* **`loose`**: Use when your codemod modifies structures where order doesn't matter semantically.

### Supported languages

| Language       | Features                                                       |
| -------------- | -------------------------------------------------------------- |
| JavaScript/JSX | Objects, imports, exports                                      |
| TypeScript/TSX | Objects, imports, exports, type definitions, interfaces        |
| Python         | Dictionaries, imports, keyword arguments, type unions          |
| Go             | Imports, struct literals (keyed), interface definitions        |
| Rust           | Struct expressions, derive attributes, use lists, trait bounds |
| JSON           | Objects                                                        |

<Warning>
  AST comparison preserves order when it matters semantically. For example, spread operators (`{...x, a: 1}` vs `{a: 1, ...x}`) maintain their order because they affect property override behavior.
</Warning>

## Best practices

* **Start simple**: Begin with basic transformations before complex ones
* **Test edge cases**: Include empty files, files with comments, and malformed code
* **Use descriptive names**: Make test cases self-documenting
* **Keep tests focused**: One transformation per test case
* **Validate locally**: Always test before publishing
* **Use `--strictness loose`**: When testing transforms that may produce semantically equivalent outputs with different formatting or ordering
