Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ configure(subprojects.filter { it.name in publishModules }) {
}
}

tasks.register<Copy>("copyIslToPlugin") {
group = "build"
description = "Build isl-cmd fat JAR and copy to plugin/lib for extension use"
dependsOn(":isl-cmd:shadowJar")
from(project(":isl-cmd").tasks.named("shadowJar").map { (it as org.gradle.api.tasks.bundling.Jar).archiveFile })
into(file("plugin/lib"))
rename { "isl-cmd-all.jar" }
}

tasks.register("publishToMavenCentral") {
group = "publishing"
description = "Publish all modules to Maven Central"
Expand Down
56 changes: 54 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ For development, you can run directly with Gradle:

## Basic Usage

The ISL CLI has three main commands:
The ISL CLI has four main commands:

### 1. Transform Command

Expand Down Expand Up @@ -138,7 +138,38 @@ isl validate script.isl
# If invalid, shows error details and exit code 1
```

### 3. Info Command
### 3. Test Command

Run ISL unit tests. Discovers `.isl` files containing `@test` or `@setup` annotations and executes them.

```bash
# Run tests in current directory (default: **/*.isl)
isl test

# Run tests in a specific path (directory, file, or glob)
isl test tests/
isl test tests/sample.isl

# Custom glob pattern
isl test tests/ --glob "**/*.test.isl"

# Write results to JSON file
isl test -o results.json
```

**Options:**

| Option | Description |
|--------|-------------|
| `path` | Directory, file, or glob to search (default: current directory) |
| `--glob PATTERN` | Glob to filter files when path is a directory (default: `**/*.isl`) |
| `-o, --output FILE` | Write test results to JSON file |

Exit code: 0 if all tests pass, 1 if any fail.

See [Unit Testing](../ext/unit-testing/index.md) for writing tests, assertions, and loading fixtures.

### 4. Info Command

Display version and system information:

Expand Down Expand Up @@ -176,6 +207,27 @@ isl info
debug=true isl transform script.isl -i input.json
```

### Logging from ISL Scripts

When running transforms or tests from the CLI, you can log messages from your ISL scripts:

```isl
@.Log.Info("Processing started")
@.Log.Info("Item count:", $count)
@.Log.Warn("Unexpected value:", $value)
@.Log.Error("Failed:", $error)
@.Log.Debug("Debug info") // Only outputs when -Ddebug=true
```

| Function | Output | When |
|----------|--------|------|
| `@.Log.Debug(...)` | stdout | Only when `-Ddebug=true` |
| `@.Log.Info(...)` | stdout | Always |
| `@.Log.Warn(...)` | stderr | Always |
| `@.Log.Error(...)` | stderr | Always |

All functions accept multiple arguments (strings, variables, expressions); they are joined with spaces. JSON objects are pretty-printed.

## Working with Input Data

### Using Input Files
Expand Down
107 changes: 95 additions & 12 deletions docs/ext/unit-testing/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,116 @@ grand_parent: Advanced Topics
nav_order: 3
---

## Test
# Test Annotations

To create a unit test, we need to utilise the `@test` annotation to denote that the function
is a unit test.
## @test

```kotlin
Marks a function as a unit test. The function runs as a test case when tests are executed.

### Basic form

```isl
@test
fun test_addNumbers() {
...
$sum: 1 + 2;
@.Assert.equal(3, $sum);
}
```

When no parameters are given, the function name is used as the test name.

### Custom display name

```isl
@test("Addition of positive numbers")
fun test_addNumbers() {
@.Assert.equal(3, 1 + 2);
}
```

In the test function, we can write regular ISL code, along with additional functions specific for testing capabilities.
### Name and group

```isl
@test("Check total", "math")
fun test_total() {
@.Assert.equal(10, 5 + 5);
}
```

### Object form

```isl
@test({ name: "Grouped test", group: "math" })
fun test_grouped() {
$value: 30;
@.Assert.equal(30, $value);
}
```

Use the object form when you need both a custom name and group.

### Parameter summary

| Form | Example | Result |
|------|---------|--------|
| No params | `@test` | Test name = function name |
| String | `@test("My test")` | Test name = "My test" |
| Two strings | `@test("Name", "group")` | Custom name and group |
| Object | `@test({ name: "x", group: "y" })` | Custom name and group |

## @setup

## Setup
Marks a function to run **before each test** in the same file. Use it for shared initialization.

We can also use the `@setup` annotation to denote actions we wish to repeat for each unit tests (e.g. setup of something specific)
- At most **one** `@setup` function per file
- Runs before every `@test` function in that file
- Does not run as a test itself

```kotlin
```isl
@setup
fun setup() {
// Perform actions required before executing test
$sharedState: { count: 0 };
// This runs before each test
}

@test
fun test_first() {
// setup() already ran
@.Assert.equal(1, 1);
}

@test
fun test_addNumbers() {
...
fun test_second() {
// setup() runs again before this test
@.Assert.equal(2, 2);
}
```

## File structure

A typical test file:

```isl
// Optional: imports
import Helper from "../helper.isl";

@setup
fun setup() {
// Runs before each test
}

@test
fun test_basic() {
// Test code
}

@test("Custom name")
fun test_withName() {
// Test code
}

@test({ name: "Edge case", group: "validation" })
fun test_edgeCase() {
// Test code
}
```
134 changes: 75 additions & 59 deletions docs/ext/unit-testing/assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,100 +5,116 @@ grand_parent: Advanced Topics
nav_order: 2
---

In order to verify values within the unit tests, we can utilise the Assertion framework.
# Assertions

These are accessible under the `@.Assert` namespace.
Assertions verify values in unit tests. They are available under the `@.Assert` namespace. All assertion methods accept an optional third parameter `message` for custom failure output.

## Equal
## Equality

### Description
### equal

Verifies whether or not values are equal.
Verifies that two values are equal (deep equality for objects and arrays; property order is ignored for objects).

### Syntax
**Syntax:** `@.Assert.equal(expected, actual, message?)`

`@.Assert.Equal($expectedValue, $actualValue, $msg)`

- `$expectedValue`: Expected value.
- `$actualValue`: Actual value to verify against.
- `$msg`: Optional error message to show if assertion fails.

### Example

```kotlin
```isl
@test
fun assert_equals() {
$var1 : 20
@.Assert.Equal(40, $var1, "Values don't match.");
fun test_equal() {
$var1: 20;
@.Assert.equal(20, $var1);

$obj1: { a: 1, b: 2 };
$obj2: { b: 2, a: 1 };
@.Assert.equal($obj1, $obj2); // Objects equal despite property order
}
```

## NotEqual

### Description

Verifies whether or not values are not equal.

### Syntax
### notEqual

`@.Assert.NotEqual($expectedValue, $actualValue, $msg)`
Verifies that two values are not equal.

- `$expectedValue`: Expected value.
- `$actualValue`: Actual value to verify against.
- `$msg`: Optional error message to show if assertion fails.
**Syntax:** `@.Assert.notEqual(expected, actual, message?)`

### Example

```kotlin
```isl
@test
fun assert_equals() {
$var1 : 20
@.Assert.NotEqual(40, $var1, "Values match.");
fun test_notEqual() {
$var1: 20;
@.Assert.notEqual(40, $var1, "Values should differ");
}
```

## NotNull
## Null Checks

### Description
### notNull

Verifies whether or not value is not null.
Verifies that a value is not null.

### Syntax
**Syntax:** `@.Assert.notNull(value, message?)`

`@.Assert.NotNull($value, $msg)`
```isl
@test
fun test_notNull() {
$var1: 42;
@.Assert.notNull($var1, "Value should not be null");
}
```

- `$value`: Expected value.
- `$msg`: Optional error message to show if assertion fails.
### isNull

### Example
Verifies that a value is null.

```kotlin
**Syntax:** `@.Assert.isNull(value, message?)`

```isl
@test
fun assert_equals() {
$var1 : null
@.Assert.NotNull($var1, "Value is null");
fun test_isNull() {
$var1: null;
@.Assert.isNull($var1, "Value should be null");
}
```

## IsNull
## Comparisons

| Assertion | Description |
|-----------|-------------|
| `@.Assert.lessThan(a, b)` | a < b |
| `@.Assert.lessThanOrEqual(a, b)` | a <= b |
| `@.Assert.greaterThan(a, b)` | a > b |
| `@.Assert.greaterThanOrEqual(a, b)` | a >= b |

### Description
## String and Pattern

Verifies whether or not value is null.
| Assertion | Description |
|-----------|-------------|
| `@.Assert.matches(pattern, value)` | value matches regex pattern |
| `@.Assert.notMatches(pattern, value)` | value does not match pattern |
| `@.Assert.contains(expected, actual)` | actual contains expected |
| `@.Assert.notContains(expected, actual)` | actual does not contain expected |
| `@.Assert.startsWith(prefix, value)` | value starts with prefix |
| `@.Assert.notStartsWith(prefix, value)` | value does not start with prefix |
| `@.Assert.endsWith(suffix, value)` | value ends with suffix |
| `@.Assert.notEndsWith(suffix, value)` | value does not end with suffix |

### Syntax
## Membership and Type

`@.Assert.IsNull($value, $msg)`
| Assertion | Description |
|-----------|-------------|
| `@.Assert.in(value, collection)` | value is in collection |
| `@.Assert.notIn(value, collection)` | value is not in collection |
| `@.Assert.isType(value, type)` | value is of type (e.g. `number`, `string`, `array`, `node`, `date`) |
| `@.Assert.isNotType(value, type)` | value is not of type |

- `$value`: Expected value.
- `$msg`: Optional error message to show if assertion fails.
## Custom Failure Message

### Example
All assertions accept an optional third parameter for a custom message:

```kotlin
```isl
@test
fun assert_equals() {
$var1 : null
@.Assert.NotNull($var1, "Value not null");
fun test_withMessage() {
$var1: 1;
$var2: 2;
@.Assert.equal($var1, $var2, "Expected 1 to equal 2 - values mismatch");
}
```

When the assertion fails, the custom message is included in the output.
Loading
Loading