• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fslev / cucumber-jutils / #287

05 May 2026 01:59PM UTC coverage: 87.5% (-1.9%) from 89.399%
#287

push

fslevoaca-ionos
Migrate to Java 17, JUnit Jupiter 6, and Mockito 5; refactor internals with modern Java idioms

73 of 84 new or added lines in 7 files covered. (86.9%)

4 existing lines in 1 file now uncovered.

245 of 280 relevant lines covered (87.5%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.15
/src/main/java/com/cucumber/utils/context/vars/ScenarioVars.java
1
package com.cucumber.utils.context.vars;
2

3
import com.fasterxml.jackson.core.JsonPointer;
4
import com.fasterxml.jackson.databind.JsonNode;
5
import io.cucumber.guice.ScenarioScoped;
6
import io.json.compare.util.JsonUtils;
7
import org.apache.logging.log4j.LogManager;
8
import org.apache.logging.log4j.Logger;
9

10
import java.io.IOException;
11
import java.util.EnumSet;
12
import java.util.HashMap;
13
import java.util.Map;
14
import java.util.Set;
15
import java.util.UUID;
16
import java.util.concurrent.ThreadLocalRandom;
17
import java.util.regex.Pattern;
18

19
@ScenarioScoped
20
public class ScenarioVars {
1✔
21

22
    private static final Logger LOG = LogManager.getLogger();
1✔
23
    private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_$\\-.@]+");
1✔
24

25
    private final Map<String, Object> vars = new HashMap<>();
1✔
26

27
    public String getAsString(String name) {
28
        Object val = get(name);
1✔
29
        return val != null ? val.toString() : null;
1✔
30
    }
31

32
    public Object get(String name) {
33
        if (name == null) {
1✔
34
            return vars.get(null);
×
35
        }
36
        String trimmedName = name.trim();
1✔
37
        return switch (trimmedName.toLowerCase()) {
1✔
38
            case "uid" -> UUID.randomUUID().toString();
1✔
39
            case "now" -> System.currentTimeMillis();
1✔
NEW
40
            case "short-random" -> ThreadLocalRandom.current().nextInt(Short.MAX_VALUE);
×
NEW
41
            case "int-random" -> ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
×
42
            default -> resolve(name, trimmedName);
1✔
43
        };
44
    }
45

46
    public void put(String name, Object val) {
47
        String trimmedName = (name == null) ? null : name.trim();
1✔
48
        if (trimmedName == null || !NAME_PATTERN.matcher(trimmedName).matches()) {
1✔
49
            throw new RuntimeException("Invalid variable name: " + trimmedName + ". Allowed pattern: " + NAME_PATTERN);
×
50
        }
51
        if (vars.containsKey(trimmedName)) {
1✔
52
            LOG.warn("Scenario variable \"{}\" will be overridden with {}", trimmedName, val);
1✔
53
        }
54
        vars.put(trimmedName, val);
1✔
55
    }
1✔
56

57
    public void putAll(Map<String, Object> vars) {
58
        vars.forEach(this::put);
1✔
59
    }
1✔
60

61
    public Set<String> nameSet() {
62
        return vars.keySet();
×
63
    }
64

65
    public boolean containsVariable(String name) {
66
        return vars.containsKey(name) || (isJsonPointerExpression(name) && getJsonPointerValue(name) != null);
1✔
67
    }
68

69
    public int size() {
70
        return vars.size();
×
71
    }
72

73
    public enum FileExtension {
1✔
74
        PROPERTIES(".properties"),
1✔
75
        YAML(".yaml"),
1✔
76
        YML(".yml"),
1✔
77
        JSON(".json"),
1✔
78
        XML(".xml"),
1✔
79
        TXT(".txt"),
1✔
80
        CSV(".csv"),
1✔
81
        HTML(".html"),
1✔
82
        TEXT(".text");
1✔
83

84
        private static final Set<FileExtension> VAR_TYPES = EnumSet.of(JSON, XML, TXT, CSV, HTML, TEXT);
1✔
85
        private static final String[] ALL = toValueArray(EnumSet.allOf(FileExtension.class));
1✔
86
        private static final String[] VAR_ONLY = toValueArray(VAR_TYPES);
1✔
87

88
        private final String name;
89

90
        FileExtension(String name) {
1✔
91
            this.name = name;
1✔
92
        }
1✔
93

94
        public static String[] allExtensions() {
95
            return ALL.clone();
1✔
96
        }
97

98
        public static String[] varFileExtensions() {
99
            return VAR_ONLY.clone();
1✔
100
        }
101

102
        public String value() {
103
            return name;
1✔
104
        }
105

106
        private static String[] toValueArray(Set<FileExtension> set) {
107
            return set.stream().map(FileExtension::value).toArray(String[]::new);
1✔
108
        }
109
    }
110

111
    @Override
112
    public String toString() {
113
        return this.vars.toString();
×
114
    }
115

116
    private Object resolve(String name, String trimmedName) {
117
        Object value = isJsonPointerExpression(name) ? getJsonPointerValue(name) : vars.get(trimmedName);
1✔
118
        return value instanceof String s ? ScenarioVarsParser.parse(s, this) : value;
1✔
119
    }
120

121
    private Object getJsonPointerValue(String varName) {
122
        String[] paths = varName.split(String.valueOf(JsonPointer.SEPARATOR), 2);
1✔
123
        String rootPath = paths[0];
1✔
124
        if (!vars.containsKey(rootPath)) {
1✔
125
            return null;
1✔
126
        }
127
        Object rootValue = vars.get(rootPath);
1✔
128
        try {
129
            JsonNode rootJsonValue = JsonUtils.toJson(rootValue);
1✔
130
            JsonNode jsonValue = rootJsonValue.at(JsonPointer.SEPARATOR + paths[1]);
1✔
131
            if (jsonValue.isMissingNode()) {
1✔
132
                return null;
1✔
133
            }
134
            return rootValue instanceof String
1✔
135
                    ? (jsonValue.isValueNode() ? jsonValue.asText() : jsonValue.toString())
1✔
136
                    : jsonValue;
1✔
NEW
137
        } catch (IOException e) {
×
NEW
138
            return null;
×
139
        }
140
    }
141

142
    private static boolean isJsonPointerExpression(String varName) {
143
        return varName.contains(String.valueOf(JsonPointer.SEPARATOR));
1✔
144
    }
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc