Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,23 @@
"unlink-dist": "cd dist && npm unlink && rm package*",
"watch": "NODE_ENV=development rollup --watch -c",
"test": "react-scripts test --transformIgnorePatterns /\"node_modules/(?!axios)/\"",
"test:coverage": "CI=true react-scripts test --coverage --watchAll=false --transformIgnorePatterns /\"node_modules/(?!axios)/\"",
"eject": "react-scripts eject",
"lint": "eslint src/ --ext .js --max-warnings=0",
"format": "prettier --write \"src/**/*.js\""
},
"jest": {
"collectCoverageFrom": [
"src/lib/**/*.{js,jsx}",
"!src/lib/**/*.d.ts",
"!src/demos/**/*"
],
"coverageReporters": [
"text",
"lcov",
"html"
]
},
"peerDependencies": {
"@babel/runtime": "^7.9.0",
"axios": "^1.7.7",
Expand All @@ -48,6 +61,7 @@
"@rollup/plugin-node-resolve": "^9.0.0",
"axios": "^1.7.7",
"axios-mock-adapter": "^1.18.0",
"baseline-browser-mapping": "^2.9.19",
"cheerio": "1.0.0-rc.3",
"coveralls": "^3.0.0",
"enzyme": "^3.10.0",
Expand Down
98 changes: 98 additions & 0 deletions src/lib/api/UrlParamValidator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { UrlParamValidator } from "./UrlParamValidator";

describe("UrlParamValidator", () => {
const validator = new UrlParamValidator();
const mockUrlHandler = {
urlFilterSeparator: "-",
};

describe("isValid", () => {
it("should validate 'queryString' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "queryString", "test query")).toBe(true);
expect(validator.isValid(mockUrlHandler, "queryString", "")).toBe(true);
expect(validator.isValid(mockUrlHandler, "queryString", "search")).toBe(true);
});

it("should validate 'sortBy' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "sortBy", "bestmatch")).toBe(true);
expect(validator.isValid(mockUrlHandler, "sortBy", "name")).toBe(true);
expect(validator.isValid(mockUrlHandler, "sortBy", "date")).toBe(true);
});

it("should validate 'sortOrder' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "sortOrder", "asc")).toBe(true);
expect(validator.isValid(mockUrlHandler, "sortOrder", "desc")).toBe(true);
expect(validator.isValid(mockUrlHandler, "sortOrder", "other")).toBe(false);
expect(validator.isValid(mockUrlHandler, "sortOrder", "")).toBe(false);
});

it("should validate 'page' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "page", 1)).toBe(true);
expect(validator.isValid(mockUrlHandler, "page", 10)).toBe(true);
expect(validator.isValid(mockUrlHandler, "page", 100)).toBe(true);
expect(validator.isValid(mockUrlHandler, "page", 0)).toBe(false);
expect(validator.isValid(mockUrlHandler, "page", -1)).toBe(false);
});

it("should validate 'size' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "size", 10)).toBe(true);
expect(validator.isValid(mockUrlHandler, "size", 100)).toBe(true);
expect(validator.isValid(mockUrlHandler, "size", 1)).toBe(true);
expect(validator.isValid(mockUrlHandler, "size", 0)).toBe(false);
expect(validator.isValid(mockUrlHandler, "size", -1)).toBe(false);
});

it("should validate 'layout' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "layout", "list")).toBe(true);
expect(validator.isValid(mockUrlHandler, "layout", "grid")).toBe(true);
expect(validator.isValid(mockUrlHandler, "layout", "other")).toBe(false);
expect(validator.isValid(mockUrlHandler, "layout", "")).toBe(false);
});

it("should validate 'filters' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "filters", ["type:paper"])).toBe(true);
expect(validator.isValid(mockUrlHandler, "filters", ["type:book"])).toBe(true);
expect(validator.isValid(mockUrlHandler, "filters", ["status:open"])).toBe(true);
expect(
validator.isValid(mockUrlHandler, "filters", ["type:paper", "status:open"])
).toBe(true);
expect(validator.isValid(mockUrlHandler, "filters", ["type-paper"])).toBe(false);
expect(validator.isValid(mockUrlHandler, "filters", ["type"])).toBe(false);
expect(validator.isValid(mockUrlHandler, "filters", ["type:"])).toBe(true); // empty value is valid
expect(validator.isValid(mockUrlHandler, "filters", [":paper"])).toBe(true); // empty key is valid
});

it("should validate single filter value", () => {
expect(validator.isValid(mockUrlHandler, "filters", "type:paper")).toBe(true);
expect(validator.isValid(mockUrlHandler, "filters", "status:open")).toBe(true);
});

it("should validate 'hiddenParams' parameter correctly", () => {
expect(validator.isValid(mockUrlHandler, "hiddenParams", ["type:paper"])).toBe(
true
);
expect(validator.isValid(mockUrlHandler, "hiddenParams", "type:book")).toBe(true);
});

it("should return false for unknown parameters", () => {
expect(validator.isValid(mockUrlHandler, "unknown", "value")).toBe(false);
expect(validator.isValid(mockUrlHandler, "random", "test")).toBe(false);
});

it("should handle filters with custom separator", () => {
const customUrlHandler = {
urlFilterSeparator: ",",
};
expect(validator.isValid(customUrlHandler, "filters", ["type:paper"])).toBe(true);
expect(validator.isValid(customUrlHandler, "filters", ["status:open"])).toBe(
true
);
expect(
validator.isValid(customUrlHandler, "filters", ["type:paper,status:open"])
).toBe(true);
expect(
validator.isValid(customUrlHandler, "filters", ["type:paper", "status:open"])
).toBe(true);
});
});
});
43 changes: 43 additions & 0 deletions src/lib/api/contrib/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of React-SearchKit.
* Copyright (C) 2018-2022 CERN.
*
* React-SearchKit is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import * as Contrib from "./index";

describe("contrib/index", () => {
it("should export OSRequestSerializer from opensearch", () => {
expect(Contrib.OSRequestSerializer).toBeDefined();
});

it("should export OSResponseSerializer from opensearch", () => {
expect(Contrib.OSResponseSerializer).toBeDefined();
});

it("should export OSSearchApi from opensearch", () => {
expect(Contrib.OSSearchApi).toBeDefined();
});

it("should export InvenioRequestSerializer from invenio", () => {
expect(Contrib.InvenioRequestSerializer).toBeDefined();
});

it("should export InvenioResponseSerializer from invenio", () => {
expect(Contrib.InvenioResponseSerializer).toBeDefined();
});

it("should export InvenioSearchApi from invenio", () => {
expect(Contrib.InvenioSearchApi).toBeDefined();
});

it("should export InvenioSuggestionApi from invenio", () => {
expect(Contrib.InvenioSuggestionApi).toBeDefined();
});

it("should export InvenioRecordsResourcesRequestSerializer from invenio", () => {
expect(Contrib.InvenioRecordsResourcesRequestSerializer).toBeDefined();
});
});
140 changes: 140 additions & 0 deletions src/lib/api/contrib/invenio/InvenioResponseSerializer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* This file is part of React-SearchKit.
* Copyright (C) 2018-2022 CERN.
*
* React-SearchKit is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import { InvenioResponseSerializer } from "./InvenioResponseSerializer";

describe("InvenioResponseSerializer", () => {
const serializer = new InvenioResponseSerializer();

it("should exist and have serialize method", () => {
expect(serializer).toBeDefined();
expect(typeof serializer.serialize).toBe("function");
});

it("should serialize a complete response payload", () => {
const payload = {
hits: {
hits: [
{ id: 1, title: "Result 1" },
{ id: 2, title: "Result 2" },
],
total: 2,
},
aggregations: {
type: {
buckets: [
{ key: "paper", doc_count: 10 },
{ key: "book", doc_count: 5 },
],
},
},
otherField: "value",
};

const result = serializer.serialize(payload);

expect(result).toEqual({
aggregations: {
type: {
buckets: [
{ key: "paper", doc_count: 10 },
{ key: "book", doc_count: 5 },
],
},
},
hits: [
{ id: 1, title: "Result 1" },
{ id: 2, title: "Result 2" },
],
total: 2,
extras: {
otherField: "value",
},
});
});

it("should handle response without aggregations", () => {
const payload = {
hits: {
hits: [{ id: 1, title: "Result 1" }],
total: 1,
},
};

const result = serializer.serialize(payload);

expect(result.aggregations).toEqual({});
expect(result.hits).toEqual([{ id: 1, title: "Result 1" }]);
expect(result.total).toBe(1);
expect(result.extras).toEqual({});
});

it("should handle empty hits array", () => {
const payload = {
hits: {
hits: [],
total: 0,
},
aggregations: {},
};

const result = serializer.serialize(payload);

expect(result.hits).toEqual([]);
expect(result.total).toBe(0);
});

it("should handle payload with total count string (ES format)", () => {
const payload = {
hits: {
hits: [],
total: "0", // Elasticsearch returns string
},
};

const result = serializer.serialize(payload);

expect(result.total).toBe("0");
});

it("should include all extra fields in extras", () => {
const payload = {
hits: {
hits: [],
total: 0,
},
aggregations: {},
field1: "value1",
field2: 123,
field3: { nested: true },
};

const result = serializer.serialize(payload);

expect(result.extras).toEqual({
field1: "value1",
field2: 123,
field3: { nested: true },
});
});

it("should not mutate the original payload", () => {
const payload = {
hits: {
hits: [{ id: 1 }],
total: 1,
},
aggregations: {},
};

const payloadCopy = JSON.parse(JSON.stringify(payload));
serializer.serialize(payload);

expect(payload).toEqual(payloadCopy);
});
});
70 changes: 70 additions & 0 deletions src/lib/api/contrib/invenio/InvenioSuggestionApi.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* This file is part of React-SearchKit.
* Copyright (C) 2019 CERN.
*
* React-SearchKit is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import { InvenioSuggestionApi } from "./InvenioSuggestionApi";

describe("InvenioSuggestionApi", () => {
const validConfig = {
axios: { url: "http://test.com" },
invenio: {
suggestions: {
queryField: "title",
responseField: "title",
},
},
};

it("should create instance with valid config", () => {
expect(() => new InvenioSuggestionApi(validConfig)).not.toThrow();
});

it("should exist as class", () => {
expect(InvenioSuggestionApi).toBeDefined();
expect(typeof InvenioSuggestionApi).toBe("function");
});

it("should initialize request serializer with queryField", () => {
const api = new InvenioSuggestionApi(validConfig);
expect(api.requestSerializer).toBeDefined();
expect(api.requestSerializer.queryField).toBe("title");
});

it("should serialize query correctly", () => {
const api = new InvenioSuggestionApi(validConfig);
const result = api.requestSerializer.serialize({ suggestionString: "test" });
expect(result).toBe("q=title:test");
});

it("should serialize query with null suggestionString", () => {
const api = new InvenioSuggestionApi(validConfig);
const result = api.requestSerializer.serialize({ suggestionString: null });
expect(result).toBe("");
});

it("should deserialize response correctly", () => {
const api = new InvenioSuggestionApi(validConfig);
const payload = {
hits: {
hits: [{ metadata: { title: "Paper 1" } }, { metadata: { title: "Paper 2" } }],
},
};
const result = api.responseSerializer.serialize(payload);
expect(result).toEqual({
suggestions: ["Paper 1", "Paper 2"],
});
});

it("should handle empty response", () => {
const api = new InvenioSuggestionApi(validConfig);
const payload = { hits: { hits: [] } };
const result = api.responseSerializer.serialize(payload);
expect(result).toEqual({
suggestions: [],
});
});
});
Loading
Loading