Class StepUtils

java.lang.Object
io.karatelabs.core.StepUtils

public class StepUtils extends Object
Static utility methods for step execution.
  • Method Details

    • parseFeaturePath

      public static StepUtils.ParsedFeaturePath parseFeaturePath(String rawPath)
      Parse a feature path into path and tag/line components. Supports: - file.feature@tag - call specific scenario by tag - file.feature:10 / file.feature:10:25 - call by line number(s); matches Runner.path(...) suite-side syntax. Tag and line are mutually exclusive on the same path; tag suffix wins by parse order. - @tag - call scenario in same file by tag - file.feature - normal call without tag
      Parameters:
      rawPath - the raw path string (may contain @tag or :N suffix)
      Returns:
      ParsedFeaturePath with separated path and selector components
    • isValidVariableName

      public static boolean isValidVariableName(String name)
      Check if a string is a valid Karate variable name. Must start with letter or underscore, followed by letters, digits, or underscores.
    • findReadCloseParen

      public static int findReadCloseParen(String text)
      Find the index of the `)` that matches the leading `read(` in text, skipping anything inside single- or double-quoted strings and tracking nested parens. Returns -1 if text does not start with read( or no matching paren is found. Used to safely split read(path) arg even when the path contains characters like ) inside quotes.
    • splitCallExpr

      public static StepUtils.CallParts splitCallExpr(String text)
      Split a call expression into its callable target and argument, mirroring v1's parseCallArgs. For a read(...) expression the split is at read()'s close paren, so the argument may be either space-separated (read('x') arg) or directly appended (read('x.js')(arg) — the V1 immediately-invoke form). Otherwise the split is at the first space.
    • isLiteralFeatureRead

      public static boolean isLiteralFeatureRead(String text)
      Check if text is a literal feature file read like read('file.feature').
    • calleeDelta

      public static Map<String,Object> calleeDelta(Map<String,Object> callerVars, Map<String,Object> calleeVars)
      The bindings a called feature actually added or replaced — its contribution minus the caller state it merely inherited. A called feature runs with the caller's visible variables in scope, so calleeVars echoes them back; caching that full set leaks caller state into a shared cache (Scenario Outline example-row columns frozen to the first row, config-level Java.type(...) refs, etc.). Shared by the callonce and callSingle caches so both isolate the callee's delta the same way.

      Identity comparison is intentional: an untouched inherited binding keeps the same Object reference, while a callee def/assign produces a new one — so a key absent from the caller, or present but pointing at a different object, is part of the delta.

      Identity alone is not enough, though: a callee that assigns the very object it was handed (* def x = seed, where the caller passed its own x in as seed) leaves the binding identity-equal to the inherited one, so its own def vanished from the result. assignedNames carries the names the callee bound for itself (ScenarioRuntime.getAssignedVariables()) and is unioned in. The two rules only ever add to the delta; an inherited binding the callee never touched still stays out.

    • calleeDelta

      public static Map<String,Object> calleeDelta(Map<String,Object> callerVars, Map<String,Object> calleeVars, Set<String> assignedNames)
    • deepCopy

      public static Object deepCopy(Object value)
      Deep copy a value (Map, List, or primitive).
    • getOrCreatePath

      public static Object getOrCreatePath(Map<String,Object> target, String path)
      Navigate to or create a nested path in a map structure. Creates intermediate maps as needed.
    • setValueAtPath

      public static void setValueAtPath(Object target, String path, Object value)
      Set a value at a nested path, handling both dot notation and array brackets.
    • removeAtPath

      public static void removeAtPath(Map<String,Object> map, String path)
      Remove a value at a nested path in a map.
    • findAssignmentOperator

      public static int findAssignmentOperator(String text)
      Find the first '=' that's an assignment operator (not ==, !=, invalid input: '<'=, >=).
      Parameters:
      text - the text to search
      Returns:
      index of '=' or -1 if not found
    • hasPunctuation

      public static boolean hasPunctuation(String s)
      Check if string contains JS expression punctuation like . ( [ ] ' "
      Parameters:
      s - the string to check
      Returns:
      true if contains punctuation
    • stringify

      public static String stringify(Object value)
      Convert a value to its string representation. - null -> "null" - String -> as-is - Map/List -> JSON - Node -> XML - Other -> toString()
      Parameters:
      value - the value to stringify
      Returns:
      string representation
    • containsJsonPathWildcard

      public static boolean containsJsonPathWildcard(String s)
      True when an LHS path uses a JsonPath construct that JS can't evaluate: [*] (array wildcard), .. (recursive descent), or [?(...)] (filter). Used by `set` / `remove` to fall back to Jayway when a step like set body[*].parent = 'test' would otherwise hit the JS parser.
    • isPlainIdentifier

      public static boolean isPlainIdentifier(String s)
      True if s is a bare identifier — letter/_/$ followed by letters/digits/_/$. Used by `set` to distinguish full-replacement (`set foo = ...`) from path/JS forms (`set foo.b = ...`, `set arr[0] = ...`).
    • isPureJsonPath

      public static boolean isPureJsonPath(String lhs)
      True if lhs is a "pure" JSON path expression: an identifier followed by zero or more path segments where every segment is one of .ident, [int], [int:int], ['str'], ["str"], [*], .., [?(...)]. JS-style dynamic indices (e.g. [i], [i+1]) deliberately fail this check so they continue to route through the JS engine. Used by `set` / `remove` to route LHS paths through Jayway, which mirrors v1 semantics — most importantly, auto-vivifying intermediate objects when the target path doesn't yet exist.
    • splitVarAndJsonPath

      public static StepUtils.VarAndPath splitVarAndJsonPath(String lhs)
      Split an LHS like body[*].parent into variable body and JsonPath $[*].parent. Mirrors v1's parseVariableAndPath: variable is the leading identifier ([A-Za-z_$][A-Za-z0-9_$]*); the rest becomes the path, prefixed with $ unless it already starts with one.