Skip to content

Commit bd5098e

Browse files
committed
feat: Adjust formatting and README for fieldNames
1 parent fd63c8b commit bd5098e

3 files changed

Lines changed: 150 additions & 127 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ node_modules/
33
.env
44
dist/
55
.vscode/
6+
.claude/
67
*.log
78
auth.json
89
client_secret_*.json

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,30 @@ The validator expects the output format from `@marbec/web-auto-extractor`, which
4848
- Microdata
4949
- RDFa
5050

51+
### Validation Results
52+
53+
The validator returns an array of validation issues. Each issue object contains:
54+
55+
- `issueMessage` - Human-readable error or warning message
56+
- `severity` - Either `'ERROR'` or `'WARNING'`
57+
- `path` - Array representing the location in the data structure where the issue occurred
58+
- `fieldNames` - Array of field paths that caused the validation issue (e.g., `['price']`, `['offers.price']`, or `['aggregateRating', 'offers', 'review']` for compound conditions)
59+
- `location` - Character position in the original source (if available)
60+
61+
Example validation result:
62+
63+
```javascript
64+
[
65+
{
66+
issueMessage: 'Required attribute "price" is missing',
67+
severity: 'ERROR',
68+
path: [{ type: 'Product', index: 0 }],
69+
fieldNames: ['price'],
70+
location: '35,100',
71+
},
72+
];
73+
```
74+
5175
### Browser
5276

5377
You can run the parser and validator directly in the browser on any website using the following commands:

src/types/__tests__/base.test.js

Lines changed: 125 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -9,131 +9,129 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
import { expect } from "chai";
13-
import BaseValidator from "../base.js";
14-
15-
describe("BaseValidator", () => {
16-
describe("fieldNames property", () => {
17-
let validator;
18-
const testPath = [{ type: "TestType" }];
19-
20-
beforeEach(() => {
21-
validator = new BaseValidator({
22-
dataFormat: "jsonld",
23-
path: testPath,
24-
});
25-
});
26-
27-
describe("required()", () => {
28-
it("should include fieldNames when required attribute is missing", () => {
29-
const condition = validator.required("price");
30-
const result = condition({});
31-
32-
expect(result).to.deep.include({
33-
severity: "ERROR",
34-
issueMessage: 'Required attribute "price" is missing',
35-
});
36-
expect(result.fieldNames).to.deep.equal(["price"]);
37-
expect(result.path).to.deep.equal(testPath);
38-
});
39-
40-
it("should include fieldNames when required attribute has invalid type", () => {
41-
const condition = validator.required("price", "number");
42-
const result = condition({ price: "not-a-number" });
43-
44-
expect(result).to.deep.include({
45-
severity: "ERROR",
46-
issueMessage: 'Invalid type for attribute "price"',
47-
});
48-
expect(result.fieldNames).to.deep.equal(["price"]);
49-
});
50-
51-
it("should return null when required attribute is valid", () => {
52-
const condition = validator.required("name");
53-
const result = condition({ name: "Test Product" });
54-
55-
expect(result).to.be.null;
56-
});
57-
});
58-
59-
describe("recommended()", () => {
60-
it("should include fieldNames when recommended attribute is missing", () => {
61-
const condition = validator.recommended("description");
62-
const result = condition({});
63-
64-
expect(result).to.deep.include({
65-
severity: "WARNING",
66-
issueMessage: 'Missing field "description" (optional)',
67-
});
68-
expect(result.fieldNames).to.deep.equal(["description"]);
69-
expect(result.path).to.deep.equal(testPath);
70-
});
71-
72-
it("should include fieldNames when recommended attribute has invalid type", () => {
73-
const condition = validator.recommended("image", "url");
74-
const result = condition({ image: "data:invalid" });
75-
76-
expect(result).to.deep.include({
77-
severity: "WARNING",
78-
issueMessage: 'Invalid type for attribute "image"',
79-
});
80-
expect(result.fieldNames).to.deep.equal(["image"]);
81-
});
82-
83-
it("should return null when recommended attribute is valid", () => {
84-
const condition = validator.recommended("description");
85-
const result = condition({ description: "A great product" });
86-
87-
expect(result).to.be.null;
88-
});
89-
});
90-
91-
describe("or()", () => {
92-
it("should include fieldNames when or conditions fail", () => {
93-
const condition = validator.or(
94-
validator.required("price"),
95-
validator.required("priceSpecification.price")
96-
);
97-
const result = condition({});
98-
99-
expect(result.severity).to.equal("ERROR");
100-
expect(result.fieldNames).to.deep.equal([
101-
"price",
102-
"priceSpecification.price",
103-
]);
104-
expect(result.path).to.deep.equal(testPath);
105-
});
106-
107-
it("should return null when at least one or condition passes", () => {
108-
const condition = validator.or(
109-
validator.required("price"),
110-
validator.required("priceSpecification.price")
111-
);
112-
const result = condition({ price: "19.99" });
113-
114-
expect(result).to.be.null;
115-
});
116-
117-
it("should handle single failing condition in or()", () => {
118-
const condition = validator.or(
119-
validator.required("availability")
120-
);
121-
const result = condition({});
122-
123-
expect(result.fieldNames).to.deep.equal(["availability"]);
124-
});
125-
});
126-
127-
describe("nested path fieldNames", () => {
128-
it("should include fieldNames for nested attributes", () => {
129-
const condition = validator.required("offers.price");
130-
const result = condition({ offers: {} });
131-
132-
expect(result).to.deep.include({
133-
severity: "ERROR",
134-
});
135-
expect(result.fieldNames).to.deep.equal(["offers.price"]);
136-
});
137-
});
138-
});
12+
import { expect } from 'chai';
13+
import BaseValidator from '../base.js';
14+
15+
describe('BaseValidator', () => {
16+
describe('fieldNames property', () => {
17+
let validator;
18+
const testPath = [{ type: 'TestType' }];
19+
20+
beforeEach(() => {
21+
validator = new BaseValidator({
22+
dataFormat: 'jsonld',
23+
path: testPath,
24+
});
25+
});
26+
27+
describe('required()', () => {
28+
it('should include fieldNames when required attribute is missing', () => {
29+
const condition = validator.required('price');
30+
const result = condition({});
31+
32+
expect(result).to.deep.include({
33+
severity: 'ERROR',
34+
issueMessage: 'Required attribute "price" is missing',
35+
});
36+
expect(result.fieldNames).to.deep.equal(['price']);
37+
expect(result.path).to.deep.equal(testPath);
38+
});
39+
40+
it('should include fieldNames when required attribute has invalid type', () => {
41+
const condition = validator.required('price', 'number');
42+
const result = condition({ price: 'not-a-number' });
43+
44+
expect(result).to.deep.include({
45+
severity: 'ERROR',
46+
issueMessage: 'Invalid type for attribute "price"',
47+
});
48+
expect(result.fieldNames).to.deep.equal(['price']);
49+
});
50+
51+
it('should return null when required attribute is valid', () => {
52+
const condition = validator.required('name');
53+
const result = condition({ name: 'Test Product' });
54+
55+
expect(result).to.be.null;
56+
});
57+
});
58+
59+
describe('recommended()', () => {
60+
it('should include fieldNames when recommended attribute is missing', () => {
61+
const condition = validator.recommended('description');
62+
const result = condition({});
63+
64+
expect(result).to.deep.include({
65+
severity: 'WARNING',
66+
issueMessage: 'Missing field "description" (optional)',
67+
});
68+
expect(result.fieldNames).to.deep.equal(['description']);
69+
expect(result.path).to.deep.equal(testPath);
70+
});
71+
72+
it('should include fieldNames when recommended attribute has invalid type', () => {
73+
const condition = validator.recommended('image', 'url');
74+
const result = condition({ image: 'data:invalid' });
75+
76+
expect(result).to.deep.include({
77+
severity: 'WARNING',
78+
issueMessage: 'Invalid type for attribute "image"',
79+
});
80+
expect(result.fieldNames).to.deep.equal(['image']);
81+
});
82+
83+
it('should return null when recommended attribute is valid', () => {
84+
const condition = validator.recommended('description');
85+
const result = condition({ description: 'A great product' });
86+
87+
expect(result).to.be.null;
88+
});
89+
});
90+
91+
describe('or()', () => {
92+
it('should include fieldNames when or conditions fail', () => {
93+
const condition = validator.or(
94+
validator.required('price'),
95+
validator.required('priceSpecification.price'),
96+
);
97+
const result = condition({});
98+
99+
expect(result.severity).to.equal('ERROR');
100+
expect(result.fieldNames).to.deep.equal([
101+
'price',
102+
'priceSpecification.price',
103+
]);
104+
expect(result.path).to.deep.equal(testPath);
105+
});
106+
107+
it('should return null when at least one or condition passes', () => {
108+
const condition = validator.or(
109+
validator.required('price'),
110+
validator.required('priceSpecification.price'),
111+
);
112+
const result = condition({ price: '19.99' });
113+
114+
expect(result).to.be.null;
115+
});
116+
117+
it('should handle single failing condition in or()', () => {
118+
const condition = validator.or(validator.required('availability'));
119+
const result = condition({});
120+
121+
expect(result.fieldNames).to.deep.equal(['availability']);
122+
});
123+
});
124+
125+
describe('nested path fieldNames', () => {
126+
it('should include fieldNames for nested attributes', () => {
127+
const condition = validator.required('offers.price');
128+
const result = condition({ offers: {} });
129+
130+
expect(result).to.deep.include({
131+
severity: 'ERROR',
132+
});
133+
expect(result.fieldNames).to.deep.equal(['offers.price']);
134+
});
135+
});
136+
});
139137
});

0 commit comments

Comments
 (0)