jsxpression 0.1.8 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/analyze/analysis-error.js +5 -1
- package/lib/analyze/analysis-error.js.map +1 -1
- package/lib/analyze/analysis-report.js +5 -1
- package/lib/analyze/analysis-report.js.map +1 -1
- package/lib/analyze/analyze.js +9 -6
- package/lib/analyze/analyze.js.map +1 -1
- package/lib/analyze/analyze.test.d.ts +2 -0
- package/lib/analyze/analyze.test.js +314 -312
- package/lib/analyze/analyze.test.js.map +1 -1
- package/lib/analyze/data/analyze.js +10 -7
- package/lib/analyze/data/analyze.js.map +1 -1
- package/lib/analyze/data/data-access.js +24 -20
- package/lib/analyze/data/data-access.js.map +1 -1
- package/lib/analyze/data/index.js +5 -1
- package/lib/analyze/data/index.js.map +1 -1
- package/lib/analyze/data/method-calls.js +20 -17
- package/lib/analyze/data/method-calls.js.map +1 -1
- package/lib/analyze/data/utils.js +29 -22
- package/lib/analyze/data/utils.js.map +1 -1
- package/lib/analyze/index.js +9 -3
- package/lib/analyze/index.js.map +1 -1
- package/lib/analyze/jsx/analyze.js +11 -8
- package/lib/analyze/jsx/analyze.js.map +1 -1
- package/lib/analyze/jsx/element-attributes.js +24 -21
- package/lib/analyze/jsx/element-attributes.js.map +1 -1
- package/lib/analyze/jsx/element-children.js +23 -20
- package/lib/analyze/jsx/element-children.js.map +1 -1
- package/lib/analyze/jsx/element-tags.js +15 -12
- package/lib/analyze/jsx/element-tags.js.map +1 -1
- package/lib/analyze/jsx/index.js +5 -1
- package/lib/analyze/jsx/index.js.map +1 -1
- package/lib/analyze/security/analyze.js +13 -10
- package/lib/analyze/security/analyze.js.map +1 -1
- package/lib/analyze/security/call-expressions.js +11 -8
- package/lib/analyze/security/call-expressions.js.map +1 -1
- package/lib/analyze/security/declarations.js +15 -12
- package/lib/analyze/security/declarations.js.map +1 -1
- package/lib/analyze/security/expressions.js +17 -14
- package/lib/analyze/security/expressions.js.map +1 -1
- package/lib/analyze/security/identifiers.js +13 -10
- package/lib/analyze/security/identifiers.js.map +1 -1
- package/lib/analyze/security/index.js +5 -1
- package/lib/analyze/security/index.js.map +1 -1
- package/lib/analyze/security/member-access.js +10 -7
- package/lib/analyze/security/member-access.js.map +1 -1
- package/lib/analyze/utils.d.ts +20 -0
- package/lib/analyze/utils.js +10 -4
- package/lib/analyze/utils.js.map +1 -1
- package/lib/analyze/validation-context.d.ts +13 -0
- package/lib/analyze/validation-context.js +5 -1
- package/lib/analyze/validation-context.js.map +1 -1
- package/lib/builtins.js +20 -9
- package/lib/builtins.js.map +1 -1
- package/lib/codegen/builtins/builtin-types.js +6 -3
- package/lib/codegen/builtins/builtin-types.js.map +1 -1
- package/lib/codegen/builtins/index.js +5 -1
- package/lib/codegen/builtins/index.js.map +1 -1
- package/lib/codegen/generate-typescript-definitions.js +7 -4
- package/lib/codegen/generate-typescript-definitions.js.map +1 -1
- package/lib/codegen/generate-typescript-definitions.test.js +192 -190
- package/lib/codegen/generate-typescript-definitions.test.js.map +1 -1
- package/lib/codegen/index.js +5 -1
- package/lib/codegen/index.js.map +1 -1
- package/lib/codegen/schema/component-types.js +6 -3
- package/lib/codegen/schema/component-types.js.map +1 -1
- package/lib/codegen/schema/data-types.js +6 -3
- package/lib/codegen/schema/data-types.js.map +1 -1
- package/lib/codegen/schema/index.js +7 -2
- package/lib/codegen/schema/index.js.map +1 -1
- package/lib/codegen/schema/utils.js +8 -3
- package/lib/codegen/schema/utils.js.map +1 -1
- package/lib/compile/compilation-error.d.ts +17 -0
- package/lib/compile/compilation-error.js +5 -1
- package/lib/compile/compilation-error.js.map +1 -1
- package/lib/compile/compile.js +21 -18
- package/lib/compile/compile.js.map +1 -1
- package/lib/compile/compile.test.d.ts +2 -0
- package/lib/compile/compile.test.js +112 -110
- package/lib/compile/compile.test.js.map +1 -1
- package/lib/compile/index.js +7 -2
- package/lib/compile/index.js.map +1 -1
- package/lib/evaluate/evaluate.js +11 -8
- package/lib/evaluate/evaluate.js.map +1 -1
- package/lib/evaluate/evaluate.test.d.ts +2 -0
- package/lib/evaluate/evaluate.test.js +58 -56
- package/lib/evaluate/evaluate.test.js.map +1 -1
- package/lib/evaluate/evaluation-error.js +5 -1
- package/lib/evaluate/evaluation-error.js.map +1 -1
- package/lib/evaluate/index.js +7 -2
- package/lib/evaluate/index.js.map +1 -1
- package/lib/index.js +15 -6
- package/lib/index.js.map +1 -1
- package/lib/jsx.js +22 -10
- package/lib/jsx.js.map +1 -1
- package/lib/parse/index.js +7 -2
- package/lib/parse/index.js.map +1 -1
- package/lib/parse/parse-error.d.ts +10 -0
- package/lib/parse/parse-error.js +5 -1
- package/lib/parse/parse-error.js.map +1 -1
- package/lib/parse/parse.js +13 -7
- package/lib/parse/parse.js.map +1 -1
- package/lib/parse/parse.test.d.ts +2 -0
- package/lib/parse/parse.test.js +58 -56
- package/lib/parse/parse.test.js.map +1 -1
- package/lib/render.js +12 -9
- package/lib/render.js.map +1 -1
- package/lib/render.test.d.ts +2 -0
- package/lib/render.test.js +64 -62
- package/lib/render.test.js.map +1 -1
- package/lib/schema.js +38 -18
- package/lib/schema.js.map +1 -1
- package/lib/traverse.js +11 -8
- package/lib/traverse.js.map +1 -1
- package/lib/traverse.test.d.ts +2 -0
- package/lib/traverse.test.js +36 -34
- package/lib/traverse.test.js.map +1 -1
- package/lib/types.d.ts +12 -0
- package/lib/types.js +2 -1
- package/lib/validate.js +10 -7
- package/lib/validate.js.map +1 -1
- package/package.json +7 -7
- package/lib/codegen/builtin-types.d.ts +0 -11
- package/lib/codegen/builtin-types.d.ts.map +0 -1
- package/lib/codegen/builtin-types.js +0 -151
- package/lib/codegen/builtin-types.js.map +0 -1
- package/lib/codegen/declare-types.d.ts +0 -20
- package/lib/codegen/declare-types.d.ts.map +0 -1
- package/lib/codegen/declare-types.js +0 -299
- package/lib/codegen/declare-types.js.map +0 -1
- package/lib/codegen/schema-to-types.d.ts +0 -14
- package/lib/codegen/schema-to-types.d.ts.map +0 -1
- package/lib/codegen/schema-to-types.js +0 -203
- package/lib/codegen/schema-to-types.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const analyze_js_1 = require("./analyze.js");
|
|
5
|
+
const index_js_1 = require("../parse/index.js");
|
|
6
|
+
const builtins_js_1 = require("../builtins.js");
|
|
7
|
+
(0, vitest_1.describe)("analyze - consolidated tests", () => {
|
|
6
8
|
const baseSchema = {
|
|
7
9
|
data: {
|
|
8
10
|
user: {
|
|
@@ -69,164 +71,164 @@ describe("analyze - consolidated tests", () => {
|
|
|
69
71
|
span: { props: {} },
|
|
70
72
|
},
|
|
71
73
|
};
|
|
72
|
-
describe("data access validation", () => {
|
|
73
|
-
describe("basic data access", () => {
|
|
74
|
-
it("should allow valid data access", () => {
|
|
75
|
-
const ast = parse("<Text>{data.user.name}</Text>");
|
|
76
|
-
const result = analyze(ast, baseSchema);
|
|
77
|
-
expect(result.hasErrors).toBe(false);
|
|
78
|
-
});
|
|
79
|
-
it("should allow nested object access", () => {
|
|
80
|
-
const ast = parse("<Text>{data.user.profile.avatar}</Text>");
|
|
81
|
-
const result = analyze(ast, baseSchema);
|
|
82
|
-
expect(result.hasErrors).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
it("should block invalid data properties", () => {
|
|
85
|
-
const ast = parse("<Text>{data.user.invalid}</Text>");
|
|
86
|
-
const result = analyze(ast, baseSchema);
|
|
87
|
-
expect(result.hasErrors).toBe(true);
|
|
88
|
-
expect(result.errors[0].message).toContain("Property 'data.user.invalid' does not exist in schema");
|
|
89
|
-
});
|
|
90
|
-
it("should block invalid nested properties", () => {
|
|
91
|
-
const ast = parse("<Text>{data.user.profile.invalid}</Text>");
|
|
92
|
-
const result = analyze(ast, baseSchema);
|
|
93
|
-
expect(result.hasErrors).toBe(true);
|
|
94
|
-
expect(result.errors[0].message).toContain("Property 'data.user.profile.invalid' does not exist in schema");
|
|
74
|
+
(0, vitest_1.describe)("data access validation", () => {
|
|
75
|
+
(0, vitest_1.describe)("basic data access", () => {
|
|
76
|
+
(0, vitest_1.it)("should allow valid data access", () => {
|
|
77
|
+
const ast = (0, index_js_1.parse)("<Text>{data.user.name}</Text>");
|
|
78
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
79
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
(0, vitest_1.it)("should allow nested object access", () => {
|
|
82
|
+
const ast = (0, index_js_1.parse)("<Text>{data.user.profile.avatar}</Text>");
|
|
83
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
84
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
(0, vitest_1.it)("should block invalid data properties", () => {
|
|
87
|
+
const ast = (0, index_js_1.parse)("<Text>{data.user.invalid}</Text>");
|
|
88
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
89
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
90
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Property 'data.user.invalid' does not exist in schema");
|
|
91
|
+
});
|
|
92
|
+
(0, vitest_1.it)("should block invalid nested properties", () => {
|
|
93
|
+
const ast = (0, index_js_1.parse)("<Text>{data.user.profile.invalid}</Text>");
|
|
94
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
95
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
96
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Property 'data.user.profile.invalid' does not exist in schema");
|
|
95
97
|
});
|
|
96
98
|
});
|
|
97
|
-
describe("array access", () => {
|
|
98
|
-
it("should allow array access with valid methods", () => {
|
|
99
|
-
const ast = parse("<Text>{data.items.map(item => item)}</Text>");
|
|
100
|
-
const result = analyze(ast, baseSchema);
|
|
101
|
-
expect(result.hasErrors).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
it("should allow numeric array index access", () => {
|
|
104
|
-
const ast = parse("<Text>{data.items[0]}</Text>");
|
|
105
|
-
const result = analyze(ast, baseSchema);
|
|
106
|
-
expect(result.hasErrors).toBe(false);
|
|
107
|
-
});
|
|
108
|
-
it("should allow various numeric array indices", () => {
|
|
109
|
-
const ast = parse("<Text>{data.items[42]} and {data.numbers[1]}</Text>");
|
|
110
|
-
const result = analyze(ast, baseSchema);
|
|
111
|
-
expect(result.hasErrors).toBe(false);
|
|
112
|
-
});
|
|
113
|
-
it("should allow nested array access with indices", () => {
|
|
114
|
-
const ast = parse("<Text>{data.products[0].tags[1]}</Text>");
|
|
115
|
-
const result = analyze(ast, baseSchema);
|
|
116
|
-
expect(result.hasErrors).toBe(false);
|
|
117
|
-
});
|
|
118
|
-
it("should require array indices for nested object access", () => {
|
|
119
|
-
const ast = parse("<Text>{data.products.tags}</Text>");
|
|
120
|
-
const result = analyze(ast, baseSchema);
|
|
121
|
-
expect(result.hasErrors).toBe(true);
|
|
122
|
-
expect(result.errors[0].message).toContain("data.products.tags");
|
|
123
|
-
});
|
|
124
|
-
it("should detect typos on array properties", () => {
|
|
125
|
-
const ast = parse("<Text>{data.departments.lengdth}</Text>");
|
|
126
|
-
const result = analyze(ast, baseSchema);
|
|
127
|
-
expect(result.hasErrors).toBe(true);
|
|
128
|
-
expect(result.errors[0].code).toBe("INVALID_DATA_ACCESS");
|
|
129
|
-
expect(result.errors[0].suggestions).toContain("length");
|
|
130
|
-
});
|
|
131
|
-
it("should allow string members on array elements", () => {
|
|
132
|
-
const ast = parse("<Text>{data.items[0].length}</Text>");
|
|
133
|
-
const result = analyze(ast, baseSchema);
|
|
134
|
-
expect(result.hasErrors).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
it("should allow array.at() method with positive index", () => {
|
|
137
|
-
const ast = parse("<Text>{data.items.at(0)}</Text>");
|
|
138
|
-
const result = analyze(ast, baseSchema);
|
|
139
|
-
expect(result.hasErrors).toBe(false);
|
|
140
|
-
});
|
|
141
|
-
it("should allow array.at() method with negative index", () => {
|
|
142
|
-
const ast = parse("<Text>{data.items.at(-1)}</Text>");
|
|
143
|
-
const result = analyze(ast, baseSchema);
|
|
144
|
-
expect(result.hasErrors).toBe(false);
|
|
145
|
-
});
|
|
146
|
-
it("should allow array.at() method in complex expressions", () => {
|
|
147
|
-
const ast = parse("<Text>{data.products.at(-1).name}</Text>");
|
|
148
|
-
const result = analyze(ast, baseSchema);
|
|
149
|
-
expect(result.hasErrors).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
it("should block dynamic array access with variables", () => {
|
|
152
|
-
const ast = parse("<Text>{data.items[variable]}</Text>");
|
|
153
|
-
const result = analyze(ast, baseSchema);
|
|
154
|
-
expect(result.hasErrors).toBe(true);
|
|
155
|
-
expect(result.errors.some((e) => e.message.includes("variable") || e.message.includes("not allowed"))).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
it("should block string-based computed access on objects", () => {
|
|
158
|
-
const ast = parse("<Text>{data.user['name']}</Text>");
|
|
159
|
-
const result = analyze(ast, baseSchema);
|
|
160
|
-
expect(result.hasErrors).toBe(true);
|
|
161
|
-
expect(result.errors.some((e) => e.message.includes("computed member access not allowed"))).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
it("should block mutating array methods", () => {
|
|
164
|
-
const ast = parse("<Text>{data.items.push('new')}</Text>");
|
|
165
|
-
const result = analyze(ast, baseSchema);
|
|
166
|
-
expect(result.hasErrors).toBe(true);
|
|
167
|
-
expect(result.errors[0].message).toContain("method .push not allowed");
|
|
99
|
+
(0, vitest_1.describe)("array access", () => {
|
|
100
|
+
(0, vitest_1.it)("should allow array access with valid methods", () => {
|
|
101
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.map(item => item)}</Text>");
|
|
102
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
103
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
(0, vitest_1.it)("should allow numeric array index access", () => {
|
|
106
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items[0]}</Text>");
|
|
107
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
108
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
(0, vitest_1.it)("should allow various numeric array indices", () => {
|
|
111
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items[42]} and {data.numbers[1]}</Text>");
|
|
112
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
113
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
(0, vitest_1.it)("should allow nested array access with indices", () => {
|
|
116
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products[0].tags[1]}</Text>");
|
|
117
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
118
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
(0, vitest_1.it)("should require array indices for nested object access", () => {
|
|
121
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.tags}</Text>");
|
|
122
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
123
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
124
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("data.products.tags");
|
|
125
|
+
});
|
|
126
|
+
(0, vitest_1.it)("should detect typos on array properties", () => {
|
|
127
|
+
const ast = (0, index_js_1.parse)("<Text>{data.departments.lengdth}</Text>");
|
|
128
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
129
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
130
|
+
(0, vitest_1.expect)(result.errors[0].code).toBe("INVALID_DATA_ACCESS");
|
|
131
|
+
(0, vitest_1.expect)(result.errors[0].suggestions).toContain("length");
|
|
132
|
+
});
|
|
133
|
+
(0, vitest_1.it)("should allow string members on array elements", () => {
|
|
134
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items[0].length}</Text>");
|
|
135
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
136
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
(0, vitest_1.it)("should allow array.at() method with positive index", () => {
|
|
139
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.at(0)}</Text>");
|
|
140
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
141
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
(0, vitest_1.it)("should allow array.at() method with negative index", () => {
|
|
144
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.at(-1)}</Text>");
|
|
145
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
146
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
(0, vitest_1.it)("should allow array.at() method in complex expressions", () => {
|
|
149
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.at(-1).name}</Text>");
|
|
150
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
151
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
(0, vitest_1.it)("should block dynamic array access with variables", () => {
|
|
154
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items[variable]}</Text>");
|
|
155
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
156
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
157
|
+
(0, vitest_1.expect)(result.errors.some((e) => e.message.includes("variable") || e.message.includes("not allowed"))).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
(0, vitest_1.it)("should block string-based computed access on objects", () => {
|
|
160
|
+
const ast = (0, index_js_1.parse)("<Text>{data.user['name']}</Text>");
|
|
161
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
162
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
163
|
+
(0, vitest_1.expect)(result.errors.some((e) => e.message.includes("computed member access not allowed"))).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
(0, vitest_1.it)("should block mutating array methods", () => {
|
|
166
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.push('new')}</Text>");
|
|
167
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
168
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
169
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("method .push not allowed");
|
|
168
170
|
});
|
|
169
171
|
});
|
|
170
|
-
describe("arrow function parameter validation", () => {
|
|
171
|
-
it("should validate arrow function parameters - valid access", () => {
|
|
172
|
-
const ast = parse("<Text>{data.products.map((product, i) => product.name)}</Text>");
|
|
173
|
-
const result = analyze(ast, baseSchema);
|
|
174
|
-
expect(result.hasErrors).toBe(false);
|
|
175
|
-
});
|
|
176
|
-
it("should validate arrow function parameters - invalid property", () => {
|
|
177
|
-
const ast = parse("<Text>{data.products.map((product, i) => product.invalid)}</Text>");
|
|
178
|
-
const result = analyze(ast, baseSchema);
|
|
179
|
-
expect(result.hasErrors).toBe(true);
|
|
180
|
-
expect(result.errors[0].message).toContain("Property 'invalid' does not exist on parameter 'product'");
|
|
181
|
-
});
|
|
182
|
-
it("should validate nested arrow functions", () => {
|
|
183
|
-
const ast = parse("<Text>{data.products.map(product => product.tags.map(tag => tag))}</Text>");
|
|
184
|
-
const result = analyze(ast, baseSchema);
|
|
185
|
-
expect(result.hasErrors).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
it("should catch errors in nested arrow functions", () => {
|
|
188
|
-
const ast = parse("<Text>{data.products.map(product => product.tags.map(tag => tag.invalid))}</Text>");
|
|
189
|
-
const result = analyze(ast, baseSchema);
|
|
190
|
-
expect(result.hasErrors).toBe(true);
|
|
191
|
-
expect(result.errors[0].message).toContain("Cannot access property 'invalid' on string type");
|
|
192
|
-
});
|
|
193
|
-
it("should validate index parameters", () => {
|
|
194
|
-
const ast = parse("<Text>{data.items.map((item, i) => i + 1)}</Text>");
|
|
195
|
-
const result = analyze(ast, baseSchema);
|
|
196
|
-
expect(result.hasErrors).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
it("should catch invalid index parameter access", () => {
|
|
199
|
-
const ast = parse("<Text>{data.items.map((item, i) => i.invalid)}</Text>");
|
|
200
|
-
const result = analyze(ast, baseSchema);
|
|
201
|
-
expect(result.hasErrors).toBe(true);
|
|
202
|
-
expect(result.errors[0].message).toContain("Cannot access property 'invalid' on number type");
|
|
203
|
-
});
|
|
204
|
-
it("should handle complex nested scenarios", () => {
|
|
205
|
-
const ast = parse("<Text>{data.departments.map((dept) => dept.employees.map((employee) => employee.salary))}</Text>");
|
|
206
|
-
const result = analyze(ast, baseSchema);
|
|
207
|
-
expect(result.hasErrors).toBe(false);
|
|
208
|
-
});
|
|
209
|
-
it("should detect typos in complex nested scenarios", () => {
|
|
210
|
-
const ast = parse("<Text>{data.departments.map((dept) => dept.employees.map((employee) => employee.saladry))}</Text>");
|
|
211
|
-
const result = analyze(ast, baseSchema);
|
|
212
|
-
expect(result.hasErrors).toBe(true);
|
|
213
|
-
expect(result.errors[0].message).toContain("Property 'saladry' does not exist on parameter 'employee'");
|
|
214
|
-
expect(result.errors[0].suggestions).toEqual(["name", "role", "salary"]);
|
|
172
|
+
(0, vitest_1.describe)("arrow function parameter validation", () => {
|
|
173
|
+
(0, vitest_1.it)("should validate arrow function parameters - valid access", () => {
|
|
174
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.map((product, i) => product.name)}</Text>");
|
|
175
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
176
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
(0, vitest_1.it)("should validate arrow function parameters - invalid property", () => {
|
|
179
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.map((product, i) => product.invalid)}</Text>");
|
|
180
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
181
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
182
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Property 'invalid' does not exist on parameter 'product'");
|
|
183
|
+
});
|
|
184
|
+
(0, vitest_1.it)("should validate nested arrow functions", () => {
|
|
185
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.map(product => product.tags.map(tag => tag))}</Text>");
|
|
186
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
187
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
(0, vitest_1.it)("should catch errors in nested arrow functions", () => {
|
|
190
|
+
const ast = (0, index_js_1.parse)("<Text>{data.products.map(product => product.tags.map(tag => tag.invalid))}</Text>");
|
|
191
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
192
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
193
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Cannot access property 'invalid' on string type");
|
|
194
|
+
});
|
|
195
|
+
(0, vitest_1.it)("should validate index parameters", () => {
|
|
196
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.map((item, i) => i + 1)}</Text>");
|
|
197
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
198
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
(0, vitest_1.it)("should catch invalid index parameter access", () => {
|
|
201
|
+
const ast = (0, index_js_1.parse)("<Text>{data.items.map((item, i) => i.invalid)}</Text>");
|
|
202
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
203
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
204
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Cannot access property 'invalid' on number type");
|
|
205
|
+
});
|
|
206
|
+
(0, vitest_1.it)("should handle complex nested scenarios", () => {
|
|
207
|
+
const ast = (0, index_js_1.parse)("<Text>{data.departments.map((dept) => dept.employees.map((employee) => employee.salary))}</Text>");
|
|
208
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
209
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
(0, vitest_1.it)("should detect typos in complex nested scenarios", () => {
|
|
212
|
+
const ast = (0, index_js_1.parse)("<Text>{data.departments.map((dept) => dept.employees.map((employee) => employee.saladry))}</Text>");
|
|
213
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
214
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
215
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Property 'saladry' does not exist on parameter 'employee'");
|
|
216
|
+
(0, vitest_1.expect)(result.errors[0].suggestions).toEqual(["name", "role", "salary"]);
|
|
215
217
|
});
|
|
216
218
|
});
|
|
217
219
|
});
|
|
218
|
-
describe("method calls validation", () => {
|
|
220
|
+
(0, vitest_1.describe)("method calls validation", () => {
|
|
219
221
|
// Dynamic builtin schema tests - generates tests for all methods in BUILTINS
|
|
220
|
-
describe("builtin methods - dynamic validation", () => {
|
|
221
|
-
const builtins = getBuiltins();
|
|
222
|
+
(0, vitest_1.describe)("builtin methods - dynamic validation", () => {
|
|
223
|
+
const builtins = (0, builtins_js_1.getBuiltins)();
|
|
222
224
|
// Test each builtin object (Math, Array.prototype, String.prototype)
|
|
223
225
|
Object.entries(builtins).forEach(([objectName, schema]) => {
|
|
224
226
|
if (!schema.methods)
|
|
225
227
|
return;
|
|
226
|
-
describe(`${objectName} methods`, () => {
|
|
228
|
+
(0, vitest_1.describe)(`${objectName} methods`, () => {
|
|
227
229
|
Object.entries(schema.methods).forEach(([methodName, methodDef]) => {
|
|
228
230
|
const displayName = objectName === "Math" ? `Math.${methodName}` : methodName;
|
|
229
|
-
it(`should allow ${displayName} with valid parameters`, () => {
|
|
231
|
+
(0, vitest_1.it)(`should allow ${displayName} with valid parameters`, () => {
|
|
230
232
|
let testExpression;
|
|
231
233
|
if (objectName === "Math") {
|
|
232
234
|
// Math methods
|
|
@@ -294,15 +296,15 @@ describe("analyze - consolidated tests", () => {
|
|
|
294
296
|
// Fallback
|
|
295
297
|
testExpression = `<Text>{${objectName}.${methodName}()}</Text>`;
|
|
296
298
|
}
|
|
297
|
-
const ast = parse(testExpression);
|
|
298
|
-
const result = analyze(ast, baseSchema);
|
|
299
|
-
expect(result.hasErrors).toBe(false);
|
|
299
|
+
const ast = (0, index_js_1.parse)(testExpression);
|
|
300
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
301
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
300
302
|
});
|
|
301
303
|
// Test parameter validation for methods that have parameter constraints
|
|
302
304
|
const requiredParams = methodDef.params.filter((p) => p.required);
|
|
303
305
|
const nonVariadicParams = methodDef.params.filter((p) => !p.variadic);
|
|
304
306
|
if (requiredParams.length > 0) {
|
|
305
|
-
it(`should validate ${displayName} minimum parameters`, () => {
|
|
307
|
+
(0, vitest_1.it)(`should validate ${displayName} minimum parameters`, () => {
|
|
306
308
|
let testExpression;
|
|
307
309
|
if (objectName === "Math") {
|
|
308
310
|
testExpression = `<Text>{Math.${methodName}()}</Text>`;
|
|
@@ -316,15 +318,15 @@ describe("analyze - consolidated tests", () => {
|
|
|
316
318
|
else {
|
|
317
319
|
testExpression = `<Text>{${objectName}.${methodName}()}</Text>`;
|
|
318
320
|
}
|
|
319
|
-
const ast = parse(testExpression);
|
|
320
|
-
const result = analyze(ast, baseSchema);
|
|
321
|
-
expect(result.hasWarnings).toBe(true);
|
|
322
|
-
expect(result.warnings[0].message).toContain(`${displayName} expects at least ${requiredParams.length} parameter`);
|
|
321
|
+
const ast = (0, index_js_1.parse)(testExpression);
|
|
322
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
323
|
+
(0, vitest_1.expect)(result.hasWarnings).toBe(true);
|
|
324
|
+
(0, vitest_1.expect)(result.warnings[0].message).toContain(`${displayName} expects at least ${requiredParams.length} parameter`);
|
|
323
325
|
});
|
|
324
326
|
}
|
|
325
327
|
// Test maximum parameters for non-variadic methods
|
|
326
328
|
if (nonVariadicParams.length === methodDef.params.length && methodDef.params.length > 0) {
|
|
327
|
-
it(`should validate ${displayName} maximum parameters`, () => {
|
|
329
|
+
(0, vitest_1.it)(`should validate ${displayName} maximum parameters`, () => {
|
|
328
330
|
// Create a call with too many parameters
|
|
329
331
|
const maxParams = methodDef.params.length;
|
|
330
332
|
const extraParams = Array(maxParams + 2)
|
|
@@ -348,24 +350,24 @@ describe("analyze - consolidated tests", () => {
|
|
|
348
350
|
else {
|
|
349
351
|
testExpression = `<Text>{${objectName}.${methodName}(${extraParams})}</Text>`;
|
|
350
352
|
}
|
|
351
|
-
const ast = parse(testExpression);
|
|
352
|
-
const result = analyze(ast, baseSchema);
|
|
353
|
-
expect(result.hasWarnings).toBe(true);
|
|
354
|
-
expect(result.warnings[0].message).toContain(`${displayName} expects at most ${maxParams} parameter`);
|
|
353
|
+
const ast = (0, index_js_1.parse)(testExpression);
|
|
354
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
355
|
+
(0, vitest_1.expect)(result.hasWarnings).toBe(true);
|
|
356
|
+
(0, vitest_1.expect)(result.warnings[0].message).toContain(`${displayName} expects at most ${maxParams} parameter`);
|
|
355
357
|
});
|
|
356
358
|
}
|
|
357
359
|
});
|
|
358
360
|
});
|
|
359
361
|
});
|
|
360
362
|
});
|
|
361
|
-
describe("builtin properties - dynamic validation", () => {
|
|
362
|
-
const builtins = getBuiltins();
|
|
363
|
+
(0, vitest_1.describe)("builtin properties - dynamic validation", () => {
|
|
364
|
+
const builtins = (0, builtins_js_1.getBuiltins)();
|
|
363
365
|
Object.entries(builtins).forEach(([objectName, schema]) => {
|
|
364
366
|
if (!schema.properties)
|
|
365
367
|
return;
|
|
366
|
-
describe(`${objectName} properties`, () => {
|
|
368
|
+
(0, vitest_1.describe)(`${objectName} properties`, () => {
|
|
367
369
|
Object.entries(schema.properties).forEach(([propName, propDef]) => {
|
|
368
|
-
it(`should allow ${objectName}.${propName} property access`, () => {
|
|
370
|
+
(0, vitest_1.it)(`should allow ${objectName}.${propName} property access`, () => {
|
|
369
371
|
let testExpression;
|
|
370
372
|
if (objectName === "Array.prototype") {
|
|
371
373
|
testExpression = `<Text>{data.items.${propName}}</Text>`;
|
|
@@ -376,39 +378,39 @@ describe("analyze - consolidated tests", () => {
|
|
|
376
378
|
else {
|
|
377
379
|
testExpression = `<Text>{${objectName}.${propName}}</Text>`;
|
|
378
380
|
}
|
|
379
|
-
const ast = parse(testExpression);
|
|
380
|
-
const result = analyze(ast, baseSchema);
|
|
381
|
-
expect(result.hasErrors).toBe(false);
|
|
381
|
+
const ast = (0, index_js_1.parse)(testExpression);
|
|
382
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
383
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
382
384
|
});
|
|
383
385
|
});
|
|
384
386
|
});
|
|
385
387
|
});
|
|
386
388
|
});
|
|
387
|
-
describe("blocked methods", () => {
|
|
388
|
-
it("should block mutating array methods", () => {
|
|
389
|
+
(0, vitest_1.describe)("blocked methods", () => {
|
|
390
|
+
(0, vitest_1.it)("should block mutating array methods", () => {
|
|
389
391
|
const blockedMethods = ["push", "pop", "shift", "unshift", "splice", "reverse", "sort"];
|
|
390
392
|
blockedMethods.forEach((method) => {
|
|
391
|
-
const ast = parse(`<Text>{data.items.${method}()}</Text>`);
|
|
392
|
-
const result = analyze(ast, baseSchema);
|
|
393
|
-
expect(result.hasErrors).toBe(true);
|
|
394
|
-
expect(result.errors[0].message).toContain(`method .${method} not allowed`);
|
|
393
|
+
const ast = (0, index_js_1.parse)(`<Text>{data.items.${method}()}</Text>`);
|
|
394
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
395
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
396
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain(`method .${method} not allowed`);
|
|
395
397
|
});
|
|
396
398
|
});
|
|
397
|
-
it("should block Math.random (non-deterministic)", () => {
|
|
398
|
-
const ast = parse("<Text>{Math.random()}</Text>");
|
|
399
|
-
const result = analyze(ast, baseSchema);
|
|
400
|
-
expect(result.hasErrors).toBe(true);
|
|
401
|
-
expect(result.errors[0].message).toContain("method Math.random not allowed");
|
|
399
|
+
(0, vitest_1.it)("should block Math.random (non-deterministic)", () => {
|
|
400
|
+
const ast = (0, index_js_1.parse)("<Text>{Math.random()}</Text>");
|
|
401
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
402
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
403
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("method Math.random not allowed");
|
|
402
404
|
});
|
|
403
|
-
it("should block unknown methods", () => {
|
|
404
|
-
const ast = parse("<Text>{Math.unknownMethod()}</Text>");
|
|
405
|
-
const result = analyze(ast, baseSchema);
|
|
406
|
-
expect(result.hasErrors).toBe(true);
|
|
407
|
-
expect(result.errors[0].message).toContain("method Math.unknownMethod not allowed");
|
|
405
|
+
(0, vitest_1.it)("should block unknown methods", () => {
|
|
406
|
+
const ast = (0, index_js_1.parse)("<Text>{Math.unknownMethod()}</Text>");
|
|
407
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
408
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
409
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("method Math.unknownMethod not allowed");
|
|
408
410
|
});
|
|
409
411
|
});
|
|
410
|
-
describe("custom builtin schema", () => {
|
|
411
|
-
it("should work with custom builtin schema", () => {
|
|
412
|
+
(0, vitest_1.describe)("custom builtin schema", () => {
|
|
413
|
+
(0, vitest_1.it)("should work with custom builtin schema", () => {
|
|
412
414
|
const customSchema = {
|
|
413
415
|
data: {
|
|
414
416
|
value: { type: "number" },
|
|
@@ -418,15 +420,15 @@ describe("analyze - consolidated tests", () => {
|
|
|
418
420
|
},
|
|
419
421
|
};
|
|
420
422
|
// Math.max should work with default builtins
|
|
421
|
-
const ast1 = parse("<Text>{Math.max(data.value, 10)}</Text>");
|
|
422
|
-
const result1 = analyze(ast1, customSchema);
|
|
423
|
-
expect(result1.hasErrors).toBe(false);
|
|
423
|
+
const ast1 = (0, index_js_1.parse)("<Text>{Math.max(data.value, 10)}</Text>");
|
|
424
|
+
const result1 = (0, analyze_js_1.analyze)(ast1, customSchema);
|
|
425
|
+
(0, vitest_1.expect)(result1.hasErrors).toBe(false);
|
|
424
426
|
// Math.min should also work since we use BUILTINS
|
|
425
|
-
const ast2 = parse("<Text>{Math.min(data.value, 10)}</Text>");
|
|
426
|
-
const result2 = analyze(ast2, customSchema);
|
|
427
|
-
expect(result2.hasErrors).toBe(false);
|
|
427
|
+
const ast2 = (0, index_js_1.parse)("<Text>{Math.min(data.value, 10)}</Text>");
|
|
428
|
+
const result2 = (0, analyze_js_1.analyze)(ast2, customSchema);
|
|
429
|
+
(0, vitest_1.expect)(result2.hasErrors).toBe(false);
|
|
428
430
|
});
|
|
429
|
-
it("should allow custom String methods in builtin schema", () => {
|
|
431
|
+
(0, vitest_1.it)("should allow custom String methods in builtin schema", () => {
|
|
430
432
|
const customSchema = {
|
|
431
433
|
data: {
|
|
432
434
|
text: { type: "string" },
|
|
@@ -435,14 +437,14 @@ describe("analyze - consolidated tests", () => {
|
|
|
435
437
|
Text: { props: {} },
|
|
436
438
|
},
|
|
437
439
|
};
|
|
438
|
-
const ast1 = parse("<Text>{data.text.toUpperCase()}</Text>");
|
|
439
|
-
const result1 = analyze(ast1, customSchema);
|
|
440
|
-
expect(result1.hasErrors).toBe(false);
|
|
441
|
-
const ast2 = parse("<Text>{data.text.toLowerCase()}</Text>");
|
|
442
|
-
const result2 = analyze(ast2, customSchema);
|
|
443
|
-
expect(result2.hasErrors).toBe(false);
|
|
444
|
-
});
|
|
445
|
-
it("should fall back to default builtin schema when not specified", () => {
|
|
440
|
+
const ast1 = (0, index_js_1.parse)("<Text>{data.text.toUpperCase()}</Text>");
|
|
441
|
+
const result1 = (0, analyze_js_1.analyze)(ast1, customSchema);
|
|
442
|
+
(0, vitest_1.expect)(result1.hasErrors).toBe(false);
|
|
443
|
+
const ast2 = (0, index_js_1.parse)("<Text>{data.text.toLowerCase()}</Text>");
|
|
444
|
+
const result2 = (0, analyze_js_1.analyze)(ast2, customSchema);
|
|
445
|
+
(0, vitest_1.expect)(result2.hasErrors).toBe(false);
|
|
446
|
+
});
|
|
447
|
+
(0, vitest_1.it)("should fall back to default builtin schema when not specified", () => {
|
|
446
448
|
const schemaWithoutBuiltins = {
|
|
447
449
|
data: {
|
|
448
450
|
x: { type: "number" },
|
|
@@ -453,20 +455,20 @@ describe("analyze - consolidated tests", () => {
|
|
|
453
455
|
// No builtins specified - should use default
|
|
454
456
|
};
|
|
455
457
|
// Should work with default builtin schema
|
|
456
|
-
const ast = parse("<Text>{Math.max(data.x, 10)}</Text>");
|
|
457
|
-
const result = analyze(ast, schemaWithoutBuiltins);
|
|
458
|
-
expect(result.hasErrors).toBe(false);
|
|
458
|
+
const ast = (0, index_js_1.parse)("<Text>{Math.max(data.x, 10)}</Text>");
|
|
459
|
+
const result = (0, analyze_js_1.analyze)(ast, schemaWithoutBuiltins);
|
|
460
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
459
461
|
});
|
|
460
462
|
});
|
|
461
463
|
});
|
|
462
|
-
describe("JSX validation", () => {
|
|
463
|
-
describe("elements", () => {
|
|
464
|
-
it("should allow valid elements", () => {
|
|
465
|
-
const ast = parse("<Text>Hello</Text>");
|
|
466
|
-
const result = analyze(ast, baseSchema);
|
|
467
|
-
expect(result.hasErrors).toBe(false);
|
|
468
|
-
});
|
|
469
|
-
it("should accept enum-constrained attribute values", () => {
|
|
464
|
+
(0, vitest_1.describe)("JSX validation", () => {
|
|
465
|
+
(0, vitest_1.describe)("elements", () => {
|
|
466
|
+
(0, vitest_1.it)("should allow valid elements", () => {
|
|
467
|
+
const ast = (0, index_js_1.parse)("<Text>Hello</Text>");
|
|
468
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
469
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
470
|
+
});
|
|
471
|
+
(0, vitest_1.it)("should accept enum-constrained attribute values", () => {
|
|
470
472
|
const schema = {
|
|
471
473
|
...baseSchema,
|
|
472
474
|
elements: {
|
|
@@ -478,12 +480,12 @@ describe("analyze - consolidated tests", () => {
|
|
|
478
480
|
},
|
|
479
481
|
},
|
|
480
482
|
};
|
|
481
|
-
const ast = parse('<Badge variant="primary" />');
|
|
482
|
-
const result = analyze(ast, schema);
|
|
483
|
-
expect(result.hasWarnings).toBe(false);
|
|
484
|
-
expect(result.hasErrors).toBe(false);
|
|
483
|
+
const ast = (0, index_js_1.parse)('<Badge variant="primary" />');
|
|
484
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
485
|
+
(0, vitest_1.expect)(result.hasWarnings).toBe(false);
|
|
486
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
485
487
|
});
|
|
486
|
-
it("should report invalid enum attribute values", () => {
|
|
488
|
+
(0, vitest_1.it)("should report invalid enum attribute values", () => {
|
|
487
489
|
const schema = {
|
|
488
490
|
...baseSchema,
|
|
489
491
|
elements: {
|
|
@@ -495,31 +497,31 @@ describe("analyze - consolidated tests", () => {
|
|
|
495
497
|
},
|
|
496
498
|
},
|
|
497
499
|
};
|
|
498
|
-
const ast = parse('<Badge variant="danger" />');
|
|
499
|
-
const result = analyze(ast, schema);
|
|
500
|
-
expect(result.hasErrors).toBe(true);
|
|
501
|
-
expect(result.errors[0].code).toBe("INVALID_ATTRIBUTE_VALUE");
|
|
502
|
-
expect(result.errors[0].suggestions).toEqual(["primary", "secondary"]);
|
|
503
|
-
});
|
|
504
|
-
it("should block invalid elements", () => {
|
|
505
|
-
const ast = parse("<InvalidElement>Hello</InvalidElement>");
|
|
506
|
-
const result = analyze(ast, baseSchema);
|
|
507
|
-
expect(result.hasErrors).toBe(true);
|
|
508
|
-
expect(result.errors[0].message).toContain("Element <InvalidElement> not allowed");
|
|
509
|
-
});
|
|
510
|
-
it("should allow nested valid elements", () => {
|
|
511
|
-
const ast = parse(`
|
|
500
|
+
const ast = (0, index_js_1.parse)('<Badge variant="danger" />');
|
|
501
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
502
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
503
|
+
(0, vitest_1.expect)(result.errors[0].code).toBe("INVALID_ATTRIBUTE_VALUE");
|
|
504
|
+
(0, vitest_1.expect)(result.errors[0].suggestions).toEqual(["primary", "secondary"]);
|
|
505
|
+
});
|
|
506
|
+
(0, vitest_1.it)("should block invalid elements", () => {
|
|
507
|
+
const ast = (0, index_js_1.parse)("<InvalidElement>Hello</InvalidElement>");
|
|
508
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
509
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
510
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain("Element <InvalidElement> not allowed");
|
|
511
|
+
});
|
|
512
|
+
(0, vitest_1.it)("should allow nested valid elements", () => {
|
|
513
|
+
const ast = (0, index_js_1.parse)(`
|
|
512
514
|
<div>
|
|
513
515
|
<Text>Hello</Text>
|
|
514
516
|
<span>World</span>
|
|
515
517
|
</div>
|
|
516
518
|
`);
|
|
517
|
-
const result = analyze(ast, baseSchema);
|
|
518
|
-
expect(result.hasErrors).toBe(false);
|
|
519
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
520
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
519
521
|
});
|
|
520
522
|
});
|
|
521
|
-
describe("spread attributes", () => {
|
|
522
|
-
it("should allow spread with data object", () => {
|
|
523
|
+
(0, vitest_1.describe)("spread attributes", () => {
|
|
524
|
+
(0, vitest_1.it)("should allow spread with data object", () => {
|
|
523
525
|
const schema = {
|
|
524
526
|
data: {
|
|
525
527
|
props: {
|
|
@@ -539,11 +541,11 @@ describe("analyze - consolidated tests", () => {
|
|
|
539
541
|
},
|
|
540
542
|
},
|
|
541
543
|
};
|
|
542
|
-
const ast = parse("<Card {...data.props} />");
|
|
543
|
-
const result = analyze(ast, schema);
|
|
544
|
-
expect(result.hasErrors).toBe(false);
|
|
544
|
+
const ast = (0, index_js_1.parse)("<Card {...data.props} />");
|
|
545
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
546
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
545
547
|
});
|
|
546
|
-
it("should allow spread with nested data object", () => {
|
|
548
|
+
(0, vitest_1.it)("should allow spread with nested data object", () => {
|
|
547
549
|
const schema = {
|
|
548
550
|
data: {
|
|
549
551
|
user: {
|
|
@@ -568,11 +570,11 @@ describe("analyze - consolidated tests", () => {
|
|
|
568
570
|
},
|
|
569
571
|
},
|
|
570
572
|
};
|
|
571
|
-
const ast = parse("<Text {...data.user.attributes} />");
|
|
572
|
-
const result = analyze(ast, schema);
|
|
573
|
-
expect(result.hasErrors).toBe(false);
|
|
573
|
+
const ast = (0, index_js_1.parse)("<Text {...data.user.attributes} />");
|
|
574
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
575
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
574
576
|
});
|
|
575
|
-
it("should allow spread with arrow function parameter", () => {
|
|
577
|
+
(0, vitest_1.it)("should allow spread with arrow function parameter", () => {
|
|
576
578
|
const schema = {
|
|
577
579
|
data: {
|
|
578
580
|
items: {
|
|
@@ -604,11 +606,11 @@ describe("analyze - consolidated tests", () => {
|
|
|
604
606
|
},
|
|
605
607
|
},
|
|
606
608
|
};
|
|
607
|
-
const ast = parse("<div>{data.items.map(item => <Item {...item.props} />)}</div>");
|
|
608
|
-
const result = analyze(ast, schema);
|
|
609
|
-
expect(result.hasErrors).toBe(false);
|
|
609
|
+
const ast = (0, index_js_1.parse)("<div>{data.items.map(item => <Item {...item.props} />)}</div>");
|
|
610
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
611
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
610
612
|
});
|
|
611
|
-
it("should allow spread with the parameter itself", () => {
|
|
613
|
+
(0, vitest_1.it)("should allow spread with the parameter itself", () => {
|
|
612
614
|
const schema = {
|
|
613
615
|
data: {
|
|
614
616
|
items: {
|
|
@@ -634,11 +636,11 @@ describe("analyze - consolidated tests", () => {
|
|
|
634
636
|
},
|
|
635
637
|
},
|
|
636
638
|
};
|
|
637
|
-
const ast = parse("<div>{data.items.map(item => <Item {...item} />)}</div>");
|
|
638
|
-
const result = analyze(ast, schema);
|
|
639
|
-
expect(result.hasErrors).toBe(false);
|
|
639
|
+
const ast = (0, index_js_1.parse)("<div>{data.items.map(item => <Item {...item} />)}</div>");
|
|
640
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
641
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
640
642
|
});
|
|
641
|
-
it("should allow spread combined with regular attributes", () => {
|
|
643
|
+
(0, vitest_1.it)("should allow spread combined with regular attributes", () => {
|
|
642
644
|
const schema = {
|
|
643
645
|
data: {
|
|
644
646
|
props: {
|
|
@@ -657,11 +659,11 @@ describe("analyze - consolidated tests", () => {
|
|
|
657
659
|
},
|
|
658
660
|
},
|
|
659
661
|
};
|
|
660
|
-
const ast = parse("<Card {...data.props} highlighted={true} />");
|
|
661
|
-
const result = analyze(ast, schema);
|
|
662
|
-
expect(result.hasErrors).toBe(false);
|
|
662
|
+
const ast = (0, index_js_1.parse)("<Card {...data.props} highlighted={true} />");
|
|
663
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
664
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
663
665
|
});
|
|
664
|
-
it("should allow multiple spreads", () => {
|
|
666
|
+
(0, vitest_1.it)("should allow multiple spreads", () => {
|
|
665
667
|
const schema = {
|
|
666
668
|
data: {
|
|
667
669
|
baseProps: {
|
|
@@ -686,77 +688,77 @@ describe("analyze - consolidated tests", () => {
|
|
|
686
688
|
},
|
|
687
689
|
},
|
|
688
690
|
};
|
|
689
|
-
const ast = parse("<Card {...data.baseProps} {...data.extraProps} />");
|
|
690
|
-
const result = analyze(ast, schema);
|
|
691
|
-
expect(result.hasErrors).toBe(false);
|
|
691
|
+
const ast = (0, index_js_1.parse)("<Card {...data.baseProps} {...data.extraProps} />");
|
|
692
|
+
const result = (0, analyze_js_1.analyze)(ast, schema);
|
|
693
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
692
694
|
});
|
|
693
695
|
});
|
|
694
|
-
describe("empty expressions", () => {
|
|
695
|
-
it("should allow empty JSX expressions {} in children", () => {
|
|
696
|
-
const ast = parse("<Text>{}</Text>");
|
|
697
|
-
const result = analyze(ast, baseSchema);
|
|
698
|
-
expect(result.hasErrors).toBe(false);
|
|
699
|
-
});
|
|
700
|
-
it("should allow multiple empty expressions in children", () => {
|
|
701
|
-
const ast = parse("<Text>{}{}{}</Text>");
|
|
702
|
-
const result = analyze(ast, baseSchema);
|
|
703
|
-
expect(result.hasErrors).toBe(false);
|
|
704
|
-
});
|
|
705
|
-
it("should allow empty expressions mixed with content in children", () => {
|
|
706
|
-
const ast = parse("<Text>Hello {}{data.user.name}{} World</Text>");
|
|
707
|
-
const result = analyze(ast, baseSchema);
|
|
708
|
-
expect(result.hasErrors).toBe(false);
|
|
709
|
-
});
|
|
710
|
-
it("should handle empty expressions without compile errors", () => {
|
|
711
|
-
const ast = parse("<Text>{}</Text>");
|
|
712
|
-
const result = analyze(ast, baseSchema);
|
|
713
|
-
expect(result.hasErrors).toBe(false);
|
|
714
|
-
expect(ast).toBeDefined();
|
|
715
|
-
expect(ast.body).toHaveLength(1);
|
|
696
|
+
(0, vitest_1.describe)("empty expressions", () => {
|
|
697
|
+
(0, vitest_1.it)("should allow empty JSX expressions {} in children", () => {
|
|
698
|
+
const ast = (0, index_js_1.parse)("<Text>{}</Text>");
|
|
699
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
700
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
701
|
+
});
|
|
702
|
+
(0, vitest_1.it)("should allow multiple empty expressions in children", () => {
|
|
703
|
+
const ast = (0, index_js_1.parse)("<Text>{}{}{}</Text>");
|
|
704
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
705
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
706
|
+
});
|
|
707
|
+
(0, vitest_1.it)("should allow empty expressions mixed with content in children", () => {
|
|
708
|
+
const ast = (0, index_js_1.parse)("<Text>Hello {}{data.user.name}{} World</Text>");
|
|
709
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
710
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
711
|
+
});
|
|
712
|
+
(0, vitest_1.it)("should handle empty expressions without compile errors", () => {
|
|
713
|
+
const ast = (0, index_js_1.parse)("<Text>{}</Text>");
|
|
714
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
715
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(false);
|
|
716
|
+
(0, vitest_1.expect)(ast).toBeDefined();
|
|
717
|
+
(0, vitest_1.expect)(ast.body).toHaveLength(1);
|
|
716
718
|
});
|
|
717
719
|
});
|
|
718
720
|
});
|
|
719
|
-
describe("security validation", () => {
|
|
720
|
-
describe("disallowed identifiers", () => {
|
|
721
|
-
it("should block window access", () => {
|
|
722
|
-
const ast = parse("<Text>{window.alert('hack')}</Text>");
|
|
723
|
-
const result = analyze(ast, baseSchema);
|
|
724
|
-
expect(result.hasErrors).toBe(true);
|
|
725
|
-
expect(result.errors[0].message).toContain('Identifier "window" not allowed');
|
|
726
|
-
});
|
|
727
|
-
it("should block document access", () => {
|
|
728
|
-
const ast = parse("<Text>{document.body}</Text>");
|
|
729
|
-
const result = analyze(ast, baseSchema);
|
|
730
|
-
expect(result.hasErrors).toBe(true);
|
|
731
|
-
expect(result.errors[0].message).toContain('Identifier "document" not allowed');
|
|
721
|
+
(0, vitest_1.describe)("security validation", () => {
|
|
722
|
+
(0, vitest_1.describe)("disallowed identifiers", () => {
|
|
723
|
+
(0, vitest_1.it)("should block window access", () => {
|
|
724
|
+
const ast = (0, index_js_1.parse)("<Text>{window.alert('hack')}</Text>");
|
|
725
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
726
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
727
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain('Identifier "window" not allowed');
|
|
728
|
+
});
|
|
729
|
+
(0, vitest_1.it)("should block document access", () => {
|
|
730
|
+
const ast = (0, index_js_1.parse)("<Text>{document.body}</Text>");
|
|
731
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
732
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
733
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain('Identifier "document" not allowed');
|
|
732
734
|
});
|
|
733
735
|
});
|
|
734
|
-
describe("dangerous expressions", () => {
|
|
735
|
-
it("should block eval", () => {
|
|
736
|
-
const ast = parse("<Text>{eval('alert(1)')}</Text>");
|
|
737
|
-
const result = analyze(ast, baseSchema);
|
|
738
|
-
expect(result.hasErrors).toBe(true);
|
|
739
|
-
expect(result.errors[0].message).toContain('Identifier "eval" not allowed');
|
|
740
|
-
});
|
|
741
|
-
it("should block Function constructor", () => {
|
|
742
|
-
const ast = parse("<Text>{new Function('return 1')()}</Text>");
|
|
743
|
-
const result = analyze(ast, baseSchema);
|
|
744
|
-
expect(result.hasErrors).toBe(true);
|
|
745
|
-
expect(result.errors[0].message).toContain('Identifier "Function" not allowed');
|
|
736
|
+
(0, vitest_1.describe)("dangerous expressions", () => {
|
|
737
|
+
(0, vitest_1.it)("should block eval", () => {
|
|
738
|
+
const ast = (0, index_js_1.parse)("<Text>{eval('alert(1)')}</Text>");
|
|
739
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
740
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
741
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain('Identifier "eval" not allowed');
|
|
742
|
+
});
|
|
743
|
+
(0, vitest_1.it)("should block Function constructor", () => {
|
|
744
|
+
const ast = (0, index_js_1.parse)("<Text>{new Function('return 1')()}</Text>");
|
|
745
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
746
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
747
|
+
(0, vitest_1.expect)(result.errors[0].message).toContain('Identifier "Function" not allowed');
|
|
746
748
|
});
|
|
747
749
|
});
|
|
748
750
|
});
|
|
749
|
-
describe("integration scenarios", () => {
|
|
750
|
-
it("should catch multiple errors in complex expressions", () => {
|
|
751
|
-
const ast = parse(`
|
|
751
|
+
(0, vitest_1.describe)("integration scenarios", () => {
|
|
752
|
+
(0, vitest_1.it)("should catch multiple errors in complex expressions", () => {
|
|
753
|
+
const ast = (0, index_js_1.parse)(`
|
|
752
754
|
<InvalidElement>
|
|
753
755
|
<Text>{data.user.invalid}</Text>
|
|
754
756
|
<span>{window.location}</span>
|
|
755
757
|
</InvalidElement>
|
|
756
758
|
`);
|
|
757
|
-
const result = analyze(ast, baseSchema);
|
|
758
|
-
expect(result.hasErrors).toBe(true);
|
|
759
|
-
expect(result.errors.length).toBeGreaterThan(1);
|
|
759
|
+
const result = (0, analyze_js_1.analyze)(ast, baseSchema);
|
|
760
|
+
(0, vitest_1.expect)(result.hasErrors).toBe(true);
|
|
761
|
+
(0, vitest_1.expect)(result.errors.length).toBeGreaterThan(1);
|
|
760
762
|
});
|
|
761
763
|
});
|
|
762
764
|
});
|