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

rokucommunity / brs / #301

01 Apr 2024 08:41PM UTC coverage: 90.321% (-0.1%) from 90.465%
#301

push

web-flow
Implemented `roPath` component and fixed Interpreter Comparisons (#50)

* Added option to disable colors and fixed unit tests to not fail with colors

* Implemented `roPath` and fixed comparison to match Roku behavior

* Added unit tests and fixed prettier issues

* Properly compare Number with Boolean

* Added support to AND and OR between Boolean and Numbers

* Updated unit tests and fixed Int64

* Improved URL regex for CLI colorizer

1881 of 2239 branches covered (84.01%)

Branch coverage included in aggregate %.

167 of 181 new or added lines in 11 files covered. (92.27%)

5 existing lines in 3 files now uncovered.

5519 of 5954 relevant lines covered (92.69%)

8637.25 hits per line

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

88.61
/src/brsTypes/components/RoPath.ts
1
import { BrsValue, ValueKind, BrsString, BrsBoolean, BrsInvalid, Comparable } from "../BrsType";
141✔
2
import { BrsComponent } from "./BrsComponent";
141✔
3
import { BrsType, RoAssociativeArray, AAMember, isStringComp } from "..";
141✔
4
import { Callable, StdlibArgument } from "../Callable";
141✔
5
import { Interpreter } from "../../interpreter";
6
import path from "path";
141✔
7

8
export class RoPath extends BrsComponent implements BrsValue, Comparable {
141✔
9
    readonly kind = ValueKind.Object;
16✔
10
    private fullPath: string = "";
16✔
11
    private parsedPath: any;
12
    private parsedUrl: URL;
13
    private valid: boolean = false;
16✔
14

15
    constructor(pathName: BrsString) {
16
        super("roPath");
16✔
17
        this.parsedUrl = this.setPath(pathName.value);
16✔
18
        this.registerMethods({
16✔
19
            ifPath: [this.change, this.isValid, this.split],
20
            ifString: [this.setString, this.getString],
21
        });
22
    }
23

24
    setPath(pathName: string) {
25
        pathName = pathName.replace(/[/\\]+/g, path.posix.sep);
26✔
26
        let newUrl: URL;
27
        if (canParseURL(pathName)) {
26✔
28
            newUrl = new URL(pathName);
21✔
29
            this.fullPath = pathName;
21✔
30
            this.parsedPath = path.parse(`${newUrl.host}${newUrl.pathname}`);
21✔
31
            this.valid = true;
21✔
32
        } else {
33
            newUrl = new URL("tmp:/blank");
5✔
34
            this.fullPath = "";
5✔
35
            this.valid = false;
5✔
36
        }
37
        return newUrl;
26✔
38
    }
39
    getParentPart(): string {
40
        if (typeof this.parsedPath?.base !== "string") {
6!
NEW
41
            return this.fullPath;
×
42
        }
43
        const index = this.fullPath.indexOf(this.parsedPath.base);
6✔
44
        if (index === -1) {
6!
NEW
45
            return this.fullPath;
×
46
        }
47
        return this.fullPath.slice(0, index);
6✔
48
    }
49

50
    toString(parent?: BrsType): string {
51
        return this.fullPath;
6✔
52
    }
53

54
    getValue() {
55
        return this.fullPath;
12✔
56
    }
57

58
    lessThan(other: BrsType): BrsBoolean {
59
        if (isStringComp(other)) {
2✔
60
            return BrsBoolean.from(this.getValue() < other.getValue());
2✔
61
        }
NEW
62
        return BrsBoolean.False;
×
63
    }
64

65
    greaterThan(other: BrsType): BrsBoolean {
66
        if (isStringComp(other)) {
2✔
67
            return BrsBoolean.from(this.getValue() > other.getValue());
2✔
68
        }
NEW
69
        return BrsBoolean.False;
×
70
    }
71

72
    equalTo(other: BrsType): BrsBoolean {
73
        if (isStringComp(other)) {
4✔
74
            return BrsBoolean.from(this.getValue() === other.getValue());
4✔
75
        }
NEW
76
        return BrsBoolean.False;
×
77
    }
78

79
    concat(other: BrsType): BrsString {
80
        if (isStringComp(other)) {
2✔
81
            return new BrsString(this.getValue() + other.getValue());
2✔
82
        }
NEW
83
        return new BrsString(this.getValue() + other.toString());
×
84
    }
85

86
    /** Modifies or changes the current path via the relative or absolute path passed as a string. */
87
    private change = new Callable("change", {
16✔
88
        signature: {
89
            args: [new StdlibArgument("newPath", ValueKind.String)],
90
            returns: ValueKind.Boolean,
91
        },
92
        impl: (_: Interpreter, newPath: BrsString) => {
93
            this.setPath(newPath.value);
4✔
94
            return BrsBoolean.from(this.valid);
4✔
95
        },
96
    });
97

98
    /** Checks whether the current path is valid; that is, if the path is correctly formed. */
99
    private isValid = new Callable("isValid", {
16✔
100
        signature: {
101
            args: [],
102
            returns: ValueKind.Boolean,
103
        },
104
        impl: (_: Interpreter) => {
105
            return BrsBoolean.from(this.valid);
2✔
106
        },
107
    });
108

109
    /** Returns an roAssociativeArrays containing the significant elements of the path */
110
    private split = new Callable("split", {
16✔
111
        signature: {
112
            args: [],
113
            returns: ValueKind.Object,
114
        },
115
        impl: (_: Interpreter) => {
116
            if (this.fullPath === "") {
8✔
117
                return new RoAssociativeArray([]);
2✔
118
            }
119
            const parts = new Array<AAMember>();
6✔
120
            parts.push({
6✔
121
                name: new BrsString("basename"),
122
                value: new BrsString(this.parsedPath.name),
123
            });
124
            parts.push({
6✔
125
                name: new BrsString("extension"),
126
                value: new BrsString(this.parsedPath.ext),
127
            });
128
            parts.push({
6✔
129
                name: new BrsString("filename"),
130
                value: new BrsString(this.parsedPath.base),
131
            });
132
            parts.push({
6✔
133
                name: new BrsString("parent"),
134
                value: new BrsString(this.getParentPart()),
135
            });
136
            parts.push({
6✔
137
                name: new BrsString("phy"),
138
                value: new BrsString(this.parsedUrl.protocol),
139
            });
140
            return new RoAssociativeArray(parts);
6✔
141
        },
142
    });
143

144
    /** Sets the path string . */
145
    private setString = new Callable("SetString", {
16✔
146
        signature: {
147
            args: [new StdlibArgument("path", ValueKind.String)],
148
            returns: ValueKind.Void,
149
        },
150
        impl: (_interpreter, path: BrsString) => {
151
            this.parsedUrl = this.setPath(path.value);
6✔
152
            return BrsInvalid.Instance;
6✔
153
        },
154
    });
155

156
    /** returns string with full path */
157
    private getString = new Callable("GetString", {
16✔
158
        signature: {
159
            args: [],
160
            returns: ValueKind.String,
161
        },
162
        impl: (_interpreter) => {
163
            return new BrsString(this.fullPath);
1✔
164
        },
165
    });
166
}
167

168
function canParseURL(string: string) {
169
    try {
26✔
170
        new URL(string);
26✔
171
        return true;
21✔
172
    } catch (err) {
173
        return false;
5✔
174
    }
175
}
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