This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new a712cc5510f0 CAMEL-25416: camel-jbang - Catalog view: list the 
Kamelets, the project's own first (#27501)
a712cc5510f0 is described below

commit a712cc5510f05054a7386921e5214ed52728fa2b
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 7 14:49:47 2026 +0200

    CAMEL-25416: camel-jbang - Catalog view: list the Kamelets, the project's 
own first (#27501)
    
    The Catalog view listed components, data formats, languages, others and
    EIPs, but no Kamelets. It now lists the project's own Kamelet files, first
    and marked project, and the Kamelets of the catalog its routes use; the full
    catalog (a) lists all the Kamelets of the Kamelet catalog. The scope filter
    (f) has a kamelet kind. The detail panel shows the type of a Kamelet, where
    it comes from and its properties, and d (or o) opens its doc: how a route
    uses it and its properties with required, type, default and description.
    
    
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
    
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../ROOT/pages/camel-jbang-tui-observe.adoc        |   4 +
 .../dsl/jbang/core/commands/tui/CatalogTab.java    | 199 +++++++++++++++++++--
 .../src/main/resources/tui/help/catalog.md         |  20 ++-
 .../core/commands/tui/CatalogTabKameletsTest.java  | 116 ++++++++++++
 4 files changed, 321 insertions(+), 18 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-observe.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-observe.adoc
index 8746fc7f9035..37e82de2cfa0 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-observe.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-observe.adoc
@@ -305,6 +305,10 @@ The Catalog tab (under More > Project) lets you browse the 
full Camel component
 the TUI. Search for components by name, view their documentation, options, and 
supported headers --
 useful when building routes and you need to check what options a component 
supports.
 
+It lists Kamelets too: the project's own Kamelet files first, marked 
`project`, and the Kamelets of the catalog that the
+routes use (all of them with the full catalog, *a*). *f* cycles the kind, 
`kamelet` included, and *d* shows the
+properties of a Kamelet: required, type, default and description.
+
 == Keyboard Shortcuts
 
 === Inspect Tab
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
index 6580567d705a..2b71875c041b 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -42,6 +43,7 @@ import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.ai.KameletDefinitions;
 import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
 import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.tooling.model.EipModel;
@@ -56,7 +58,7 @@ import static 
org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.*;
  */
 class CatalogTab extends AbstractTableTab {
 
-    private static final String[] SCOPES = { "all", "component", "dataformat", 
"language", "other", "eip" };
+    private static final String[] SCOPES = { "all", "component", "dataformat", 
"language", "other", "eip", "kamelet" };
 
     private final AtomicBoolean loading = new AtomicBoolean(false);
 
@@ -254,7 +256,9 @@ class CatalogTab extends AbstractTableTab {
                     Cell.from(Span.styled(" " + name, nameStyle)),
                     Cell.from(Span.styled(entry.kind, kindStyle)),
                     Cell.from(Span.styled(entry.description, 
Style.EMPTY.dim())),
-                    Cell.from(Span.styled(entry.label != null ? entry.label : 
"", Style.EMPTY.dim()))));
+                    Cell.from(entry.project
+                            ? Span.styled(entry.label, Theme.label().bold())
+                            : Span.styled(entry.label != null ? entry.label : 
"", Style.EMPTY.dim()))));
         }
 
         if (rows.isEmpty() && dataLoaded) {
@@ -322,6 +326,17 @@ class CatalogTab extends AbstractTableTab {
         String title = " " + entry.name + " ";
 
         List<Line> lines = new ArrayList<>();
+        if (entry.kamelet != null) {
+            kameletDetail(lines, entry, area.width());
+            frame.renderWidget(
+                    Paragraph.builder()
+                            .text(Text.from(lines))
+                            
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+                                    .title(title).build())
+                            .build(),
+                    area);
+            return;
+        }
         addDetailField(lines, "Title", entry.title, area.width());
         addDetailField(lines, "Description", entry.description, area.width());
         addDetailField(lines, "Kind", entry.kind, area.width());
@@ -364,34 +379,178 @@ class CatalogTab extends AbstractTableTab {
                 area);
     }
 
-    private void openDocViewer() {
-        if (ctx.openCatalogDocCallback == null || catalog == null) {
-            return;
-        }
+    private CatalogEntry selectedEntry() {
         List<CatalogEntry> sorted = new ArrayList<>(filteredEntries);
         sorted.sort(this::sortEntry);
         Integer sel = tableState.selected();
-        if (sel == null || sel < 0 || sel >= sorted.size()) {
+        return sel == null || sel < 0 || sel >= sorted.size() ? null : 
sorted.get(sel);
+    }
+
+    private void openDocViewer() {
+        CatalogEntry entry = selectedEntry();
+        if (entry != null && entry.kamelet != null) {
+            openKameletDoc(entry);
+            return;
+        }
+        if (ctx.openCatalogDocCallback == null || catalog == null || entry == 
null) {
             return;
         }
-        CatalogEntry entry = sorted.get(sel);
         ctx.openCatalogDocCallback.accept(entry.name, entry.kind, catalog);
     }
 
     private void openOptionsViewer() {
-        if (ctx.openOptionsCallback == null || catalog == null) {
+        CatalogEntry entry = selectedEntry();
+        if (entry != null && entry.kamelet != null) {
+            // the options of a Kamelet are its properties, which its doc lists
+            openKameletDoc(entry);
             return;
         }
-        List<CatalogEntry> sorted = new ArrayList<>(filteredEntries);
-        sorted.sort(this::sortEntry);
-        Integer sel = tableState.selected();
-        if (sel == null || sel < 0 || sel >= sorted.size()) {
+        if (ctx.openOptionsCallback == null || catalog == null || entry == 
null) {
             return;
         }
-        CatalogEntry entry = sorted.get(sel);
         ctx.openOptionsCallback.accept(entry.name, entry.kind, catalog);
     }
 
+    private void openKameletDoc(CatalogEntry entry) {
+        if (ctx.openMarkdownCallback != null) {
+            ctx.openMarkdownCallback.accept(entry.name, 
kameletMarkdown(entry.kamelet));
+        }
+    }
+
+    // ---- Kamelets (CAMEL-25416) ----
+
+    private static void kameletDetail(List<Line> lines, CatalogEntry entry, 
int width) {
+        KameletDefinitions.Definition def = entry.kamelet;
+        lines.add(Line.from(Span.styled("  Kind: ", Style.EMPTY.dim()),
+                Span.styled("kamelet" + (def.type() != null ? " (" + 
def.type() + ")" : ""), kindStyle("kamelet")),
+                entry.project ? Span.styled("  the project's own", 
Theme.label().bold()) : Span.raw("")));
+        if (def.title() != null && !def.title().equals(def.name())) {
+            lines.add(Line.from(Span.styled("  Title: ", Style.EMPTY.dim()), 
Span.raw(def.title())));
+        }
+        if (def.description() != null) {
+            lines.add(Line.from(Span.styled("  Description: ", 
Style.EMPTY.dim()),
+                    Span.raw(TuiHelper.truncate(def.description(), 
Math.max(10, width - 18)))));
+        }
+        lines.add(Line.from(Span.styled("  From: ", Style.EMPTY.dim()), 
Span.raw(def.source())));
+        String props = KameletDefinitions.propertyList(def);
+        lines.add(Line.from(Span.styled("  Properties: ", Style.EMPTY.dim()),
+                Span.raw(props.isEmpty() ? "none" : TuiHelper.truncate(props, 
Math.max(10, width - 17)))));
+        lines.add(Line.from(Span.styled("  Use: ", Style.EMPTY.dim()),
+                Span.raw(("source".equals(def.type()) ? "from: " : "to: ") + 
"kamelet:" + def.name()
+                         + ", its properties under parameters:")));
+    }
+
+    /** The doc of a Kamelet: its type, where it comes from, how a route uses 
it, and its properties. */
+    static String kameletMarkdown(KameletDefinitions.Definition def) {
+        StringBuilder md = new StringBuilder();
+        md.append("# ").append(def.title() != null ? def.title() : 
def.name()).append("\n\n");
+        if (def.description() != null) {
+            md.append(def.description()).append("\n\n");
+        }
+        md.append("**Kamelet:** `").append(def.name()).append('`');
+        if (def.type() != null) {
+            md.append(" (").append(def.type()).append(')');
+        }
+        md.append("  \n**From:** ").append(def.source()).append("\n\n");
+        md.append("## Usage\n\n```yaml\n");
+        boolean source = "source".equals(def.type());
+        md.append(source ? "from:\n  uri: kamelet:" : "- to:\n    uri: 
kamelet:").append(def.name()).append('\n');
+        List<KameletDefinitions.Property> required = new ArrayList<>();
+        for (KameletDefinitions.Property p : def.properties()) {
+            if (p.required() && p.defaultValue() == null) {
+                required.add(p);
+            }
+        }
+        if (!required.isEmpty()) {
+            md.append(source ? "  parameters:\n" : "    parameters:\n");
+            for (KameletDefinitions.Property p : required) {
+                md.append(source ? "    " : "      
").append(p.name()).append(": ...\n");
+            }
+        }
+        md.append("```\n\nThe properties go under `parameters:`: they are the 
Kamelet's, not the options of the"
+                  + " component it uses.\n\n## Properties\n\n");
+        if (def.properties().isEmpty()) {
+            md.append("None.\n");
+        } else {
+            md.append("| Name | Required | Type | Default | Description 
|\n|---|---|---|---|---|\n");
+            for (KameletDefinitions.Property p : def.properties()) {
+                md.append("| ").append(p.name())
+                        .append(" | ").append(p.required() && p.defaultValue() 
== null ? "yes" : "")
+                        .append(" | ").append(p.type() != null ? p.type() : "")
+                        .append(" | ").append(p.defaultValue() != null ? 
p.defaultValue() : "")
+                        .append(" | ")
+                        .append(p.description() != null ? 
p.description().replace("|", "\\|").replace("\n", " ") : "")
+                        .append(" |\n");
+            }
+        }
+        return md.toString();
+    }
+
+    private static final java.util.regex.Pattern KAMELET_URI
+            = java.util.regex.Pattern.compile("kamelet:([a-z0-9][a-z0-9-]*)");
+
+    /**
+     * The Kamelets of the catalog view: the project's own Kamelet files 
always, the Kamelets of the catalog its routes
+     * use, and with the full catalog all the Kamelets of the catalog.
+     */
+    static List<CatalogEntry> kameletEntries(
+            Path dir, boolean full, java.util.Map<String, 
KameletDefinitions.Definition> catalogKamelets) {
+        List<CatalogEntry> entries = new ArrayList<>();
+        java.util.Map<String, KameletDefinitions.Definition> project = 
KameletDefinitions.projectKamelets(dir);
+        for (KameletDefinitions.Definition def : project.values()) {
+            entries.add(kameletEntry(def, true));
+        }
+        Set<String> used = full ? Set.of() : usedKamelets(dir);
+        for (KameletDefinitions.Definition def : catalogKamelets.values()) {
+            if (!project.containsKey(def.name()) && (full || 
used.contains(def.name()))) {
+                entries.add(kameletEntry(def, false));
+            }
+        }
+        return entries;
+    }
+
+    private static CatalogEntry kameletEntry(KameletDefinitions.Definition 
def, boolean project) {
+        CatalogEntry entry = new CatalogEntry();
+        entry.name = def.name();
+        entry.kind = "kamelet";
+        entry.title = def.title() != null ? def.title() : def.name();
+        entry.description = def.description() != null ? def.description() : "";
+        entry.label = project ? "project" : def.type();
+        entry.kamelet = def;
+        entry.project = project;
+        return entry;
+    }
+
+    /** The Kamelets the route files of the directory send to or consume from, 
kamelet:source and :sink left out. */
+    static Set<String> usedKamelets(Path dir) {
+        Set<String> used = new HashSet<>();
+        if (dir == null || !java.nio.file.Files.isDirectory(dir)) {
+            return used;
+        }
+        try (java.util.stream.Stream<Path> files = 
java.nio.file.Files.walk(dir, 6)) {
+            files.filter(f -> {
+                String n = f.getFileName().toString();
+                return (n.endsWith(".yaml") || n.endsWith(".yml") || 
n.endsWith(".java") || n.endsWith(".xml"))
+                        && !f.toString().contains("/target/") && 
!f.toString().contains("/.");
+            }).forEach(f -> {
+                try {
+                    java.util.regex.Matcher m = 
KAMELET_URI.matcher(java.nio.file.Files.readString(f));
+                    while (m.find()) {
+                        String name = m.group(1);
+                        if (!"source".equals(name) && !"sink".equals(name)) {
+                            used.add(name);
+                        }
+                    }
+                } catch (Exception e) {
+                    // an unreadable file uses none
+                }
+            });
+        } catch (Exception e) {
+            // no directory listing: none used
+        }
+        return used;
+    }
+
     private void addDetailField(List<Line> lines, String label, String value, 
int width) {
         if (value == null || value.isEmpty()) {
             return;
@@ -446,6 +605,10 @@ class CatalogTab extends AbstractTableTab {
     }
 
     private int sortEntry(CatalogEntry a, CatalogEntry b) {
+        if (a.project != b.project) {
+            // the project's own Kamelets first, whatever the sort
+            return a.project ? -1 : 1;
+        }
         int result = switch (sort) {
             case "kind" -> a.kind.compareToIgnoreCase(b.kind);
             case "description" -> 
a.description.compareToIgnoreCase(b.description);
@@ -460,6 +623,7 @@ class CatalogTab extends AbstractTableTab {
             case "dataformat" -> Theme.success();
             case "language" -> Theme.warning();
             case "eip" -> Theme.info();
+            case "kamelet" -> Theme.label();
             default -> Style.EMPTY.dim();
         };
     }
@@ -505,6 +669,10 @@ class CatalogTab extends AbstractTableTab {
                 collectArtifacts(cat, "language", cat.findLanguageNames(), 
appArtifacts, entries);
                 collectArtifacts(cat, "other", cat.findOtherNames(), 
appArtifacts, entries);
                 collectEips(cat, info.pid, full, entries);
+                // the Kamelets: the project's own first, then those of the 
catalog (CAMEL-25416)
+                Path dir = info.phantom && info.sourceDir != null
+                        ? Path.of(info.sourceDir) : 
FilesBrowser.resolveSourceDirectory(info);
+                entries.addAll(kameletEntries(dir, full, 
KameletDefinitions.catalog()));
 
                 entries.sort((a, b) -> a.name.compareToIgnoreCase(b.name));
                 applyResult(entries, cat, null);
@@ -770,5 +938,8 @@ class CatalogTab extends AbstractTableTab {
         boolean deprecated;
         String deprecatedSince;
         String deprecationNote;
+        // a Kamelet: its definition, and whether it is a file of the project
+        KameletDefinitions.Definition kamelet;
+        boolean project;
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/catalog.md 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/catalog.md
index 44a5aeec4f8f..dedc22f430a1 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/catalog.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/catalog.md
@@ -1,7 +1,7 @@
 # Catalog
 
 The Catalog tab shows Camel catalog artifacts (components, data formats,
-languages, and others). By default it shows only artifacts used by the
+languages, and others) and Kamelets. By default it shows only artifacts used 
by the
 running integration (matched via Maven dependencies). Press `a` to toggle
 to the full catalog showing all available artifacts.
 
@@ -11,12 +11,17 @@ For example, `camel-core` provides the `direct`, `seda`, 
`timer`, `bean`,
 `log`, and `mock` components, while `camel-kafka` provides the `kafka`
 component.
 
+The project's own Kamelets (its `*.kamelet.yaml` files) are always listed,
+first and marked `project`, together with the Kamelets of the catalog its
+routes use. The full catalog lists all the Kamelets of the Kamelet catalog.
+
 ## Table Columns
 
 - **NAME** — The catalog artifact name (e.g., `kafka`, `timer`, `json-jackson`)
-- **KIND** — The artifact type: `component`, `dataformat`, `language`, or 
`other`
+- **KIND** — The artifact type: `component`, `dataformat`, `language`, 
`other`, `eip` or `kamelet`
 - **DESCRIPTION** — Short description of the artifact
-- **LABEL** — Category labels (e.g., "messaging", "scheduling", 
"transformation")
+- **LABEL** — Category labels (e.g., "messaging", "scheduling", 
"transformation"); for a Kamelet,
+  `project` for the project's own, else its type (`source`, `action`, `sink`)
 
 Deprecated artifacts are shown dimmed with a "(deprecated)" suffix.
 
@@ -37,6 +42,7 @@ Press `f` to cycle the scope filter:
 - **language** — show only expression languages
 - **other** — show only miscellaneous artifacts
 - **eip** — show EIPs detected from the running app's routes
+- **kamelet** — show only Kamelets
 
 ## Filter
 
@@ -50,6 +56,11 @@ a scrollable viewer. The documentation is loaded from the 
Camel catalog
 and converted from AsciiDoc to Markdown. Use `↑`/`↓`/`PgUp`/`PgDn` or
 mouse scroll to navigate, and `Esc` to close.
 
+For a **Kamelet**, the documentation shows its type, where it comes from
+(the project file or the Kamelet catalog), how a route uses it, and its
+properties: required, type, default, and description. They go under
+`parameters:` of the `kamelet:` endpoint.
+
 ## Options
 
 Press `o` to open the options viewer for the selected artifact. This
@@ -62,7 +73,8 @@ For **components**, the viewer has three tabs:
 - **Headers** — message headers with constant names
 
 For **data formats**, **languages**, and **others**, a single Options tab
-is shown. For **EIPs**, Options and exchange Properties tabs are shown.
+is shown. For a **Kamelet**, `o` opens its documentation, whose
+properties are its options. For **EIPs**, Options and exchange Properties tabs 
are shown.
 
 Press `←`/`→` or `Tab` to switch tabs. Use `↑`/`↓`/`PgUp`/`PgDn` or
 mouse scroll to navigate, and `Esc` to close.
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTabKameletsTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTabKameletsTest.java
new file mode 100644
index 000000000000..e9f8e8f300ff
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTabKameletsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.dsl.jbang.core.commands.ai.KameletDefinitions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-25416: the Kamelets in the Catalog view, the project's own first; the 
Kamelets of the catalog its routes use,
+ * and with the full catalog all of them.
+ */
+class CatalogTabKameletsTest {
+
+    @TempDir
+    Path dir;
+
+    private static final Map<String, KameletDefinitions.Definition> CATALOG = 
new LinkedHashMap<>();
+
+    static {
+        CATALOG.put("timer-source", new KameletDefinitions.Definition(
+                "timer-source", "source", null, "Produces periodic events", 
"the Kamelet catalog 4.22.1",
+                List.of(new KameletDefinitions.Property(
+                        "message", true, "string", null, "The message to 
generate",
+                        List.of()),
+                        new KameletDefinitions.Property(
+                                "period", false, "integer", "1000", "The 
interval",
+                                List.of()))));
+        CATALOG.put("log-sink", new KameletDefinitions.Definition(
+                "log-sink", "sink", null, "Logs the events", "the Kamelet 
catalog 4.22.1", List.of()));
+    }
+
+    @Test
+    void theProjectsOwnKameletsAndTheOnesItsRoutesUse() throws Exception {
+        Files.writeString(dir.resolve("tag-order-action.kamelet.yaml"), """
+                apiVersion: camel.apache.org/v1
+                kind: Kamelet
+                metadata:
+                  name: tag-order-action
+                  labels:
+                    camel.apache.org/kamelet.type: action
+                spec:
+                  definition:
+                    title: Tag Order Action
+                    required:
+                      - tag
+                    properties:
+                      tag:
+                        title: Tag
+                        type: string
+                  template:
+                    from:
+                      uri: kamelet:source
+                      steps:
+                        - setBody:
+                            simple: "${body} [{{tag}}]"
+                """);
+        Files.writeString(dir.resolve("orders.camel.yaml"), """
+                - route:
+                    from:
+                      uri: kamelet:timer-source
+                      parameters:
+                        message: hello
+                      steps:
+                        - to:
+                            uri: kamelet:tag-order-action
+                """);
+
+        List<CatalogTab.CatalogEntry> app = CatalogTab.kameletEntries(dir, 
false, CATALOG);
+        assertThat(app).extracting(e -> 
e.name).containsExactly("tag-order-action", "timer-source");
+        assertThat(app.get(0).project).isTrue();
+        assertThat(app.get(0).label).isEqualTo("project");
+        assertThat(app.get(0).kind).isEqualTo("kamelet");
+        assertThat(app.get(1).project).isFalse();
+        assertThat(app.get(1).label).isEqualTo("source");
+        // kamelet:source in the template is not a Kamelet of the catalog
+        assertThat(CatalogTab.usedKamelets(dir)).containsOnly("timer-source", 
"tag-order-action");
+
+        List<CatalogTab.CatalogEntry> full = CatalogTab.kameletEntries(dir, 
true, CATALOG);
+        assertThat(full).extracting(e -> 
e.name).containsExactly("tag-order-action", "timer-source", "log-sink");
+    }
+
+    @Test
+    void theDocOfAKamelet() {
+        String md = CatalogTab.kameletMarkdown(CATALOG.get("timer-source"));
+        assertThat(md).contains("**Kamelet:** `timer-source` (source)")
+                .contains("**From:** the Kamelet catalog 4.22.1")
+                .contains("from:\n  uri: kamelet:timer-source\n  parameters:\n 
   message: ...")
+                .contains("| message | yes | string |  | The message to 
generate |")
+                .contains("| period |  | integer | 1000 | The interval |");
+        
assertThat(CatalogTab.kameletMarkdown(CATALOG.get("log-sink"))).contains("- 
to:\n    uri: kamelet:log-sink")
+                .contains("None.");
+    }
+}

Reply via email to