@eeacms/volto-globalsearch 1.0.18 → 1.0.20

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [1.0.20](https://github.com/eea/volto-globalsearch/compare/1.0.19...1.0.20) - 17 August 2023
8
+
9
+ #### :house: Documentation changes
10
+
11
+ - docs: Cleanup Makefile, update DEVELOP documentation, i18n - refs #254894 [valentinab25 - [`6e565ca`](https://github.com/eea/volto-globalsearch/commit/6e565ca8130e5af4b48fe13fb210665f1c874b5b)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - test: increase test coverage - refs #254313 [ana-oprea - [`889180c`](https://github.com/eea/volto-globalsearch/commit/889180c682cfb503d0d09580cbdc7b6dac932c8c)]
16
+ - instead of returning numeric values for healthcheck, return OK/WARNING/CRITICAL [Zoltan Szabo - [`9107f3b`](https://github.com/eea/volto-globalsearch/commit/9107f3beeb76cb801cd902e4843189d378a4e07b)]
17
+ ### [1.0.19](https://github.com/eea/volto-globalsearch/compare/1.0.18...1.0.19) - 26 July 2023
18
+
19
+ #### :hammer_and_wrench: Others
20
+
21
+ - extended healthcheck with info about the index & elapsed time for queries on nlp [Zoltan Szabo - [`f7fb5ae`](https://github.com/eea/volto-globalsearch/commit/f7fb5ae0afae1f3f57b4ae96547c42bd5bccaa10)]
7
22
  ### [1.0.18](https://github.com/eea/volto-globalsearch/compare/1.0.17...1.0.18) - 10 July 2023
8
23
 
9
24
  #### :hammer_and_wrench: Others
package/DEVELOP.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## Develop
4
4
 
5
+ 1. Make sure you have `docker` and `docker compose` installed and running on your machine:
6
+
7
+ ```Bash
8
+ git clone https://github.com/eea/volto-globalsearch.git
9
+ cd volto-globalsearch
10
+ git checkout -b bugfix-123456 develop
11
+ make
12
+ make start
13
+ ```
14
+
15
+ 1. Wait for `Volto started at 0.0.0.0:3000` meesage
16
+
17
+ 1. Go to http://localhost:3000
18
+
19
+ 1. Happy hacking!
20
+
21
+ ```Bash
22
+ cd src/addons/volto-globalsearch/
23
+ ```
24
+
25
+ ### Or add @eeacms/volto-globalsearch to your Volto project
26
+
5
27
  Before starting make sure your development environment is properly set. See [Volto Developer Documentation](https://docs.voltocms.com/getting-started/install/)
6
28
 
7
29
  1. Make sure you have installed `yo`, `@plone/generator-volto` and `mrs-developer`
@@ -50,3 +72,37 @@ Before starting make sure your development environment is properly set. See [Vol
50
72
 
51
73
  cd src/addons/volto-globalsearch/
52
74
  n-template/
75
+
76
+ ## Cypress
77
+
78
+ To run cypress locally, first make sure you don't have any Volto/Plone running on ports `8080` and `3000`.
79
+
80
+ You don't have to be in a `clean-volto-project`, you can be in any Volto Frontend
81
+ project where you added `volto-globalsearch` to `mrs.developer.json`
82
+
83
+ Go to:
84
+
85
+ ```BASH
86
+ cd src/addons/volto-globalsearch/
87
+ ```
88
+
89
+ Start:
90
+
91
+ ```Bash
92
+ make
93
+ make start
94
+ ```
95
+
96
+ This will build and start with Docker a clean `Plone backend` and `Volto Frontend` with `volto-globalsearch` block installed.
97
+
98
+ Open Cypress Interface:
99
+
100
+ ```Bash
101
+ make cypress-open
102
+ ```
103
+
104
+ Or run it:
105
+
106
+ ```Bash
107
+ make cypress-run
108
+ ```
package/README.md CHANGED
@@ -23,10 +23,23 @@ EEA Elastic Search Block with NLP integration [Volto](https://github.com/plone/v
23
23
 
24
24
  ## Getting started
25
25
 
26
+ ### Try volto-globalsearch with Docker
27
+
28
+ git clone https://github.com/eea/volto-globalsearch.git
29
+ cd volto-globalsearch
30
+ make
31
+ make start
32
+
33
+ Go to http://localhost:3000
34
+
26
35
  ### Add volto-globalsearch to your Volto project
27
36
 
28
37
  1. Make sure you have a [Plone backend](https://plone.org/download) up-and-running at http://localhost:8080/Plone
29
38
 
39
+ ```Bash
40
+ docker compose up backend
41
+ ```
42
+
30
43
  1. Start Volto frontend
31
44
 
32
45
  * If you already have a volto project, just update `package.json`:
@@ -0,0 +1,28 @@
1
+ version: "3"
2
+ services:
3
+ backend:
4
+ image: plone/plone-backend:${PLONE_VERSION:-6}
5
+ ports:
6
+ - "8080:8080"
7
+ environment:
8
+ SITE: "Plone"
9
+
10
+ frontend:
11
+ build:
12
+ context: ./
13
+ dockerfile: ./Dockerfile
14
+ args:
15
+ ADDON_NAME: "${ADDON_NAME}"
16
+ ADDON_PATH: "${ADDON_PATH}"
17
+ VOLTO_VERSION: ${VOLTO_VERSION:-16}
18
+ ports:
19
+ - "3000:3000"
20
+ - "3001:3001"
21
+ depends_on:
22
+ - backend
23
+ volumes:
24
+ - ./:/app/src/addons/${ADDON_PATH}
25
+ environment:
26
+ RAZZLE_INTERNAL_API_PATH: "http://backend:8080/Plone"
27
+ RAZZLE_DEV_PROXY_API_PATH: "http://backend:8080/Plone"
28
+ HOST: "0.0.0.0"
@@ -12,6 +12,7 @@ module.exports = {
12
12
  '@package/(.*)$': '<rootDir>/src/$1',
13
13
  '@root/(.*)$': '<rootDir>/src/$1',
14
14
  '@plone/volto-quanta/(.*)$': '<rootDir>/src/addons/volto-quanta/src/$1',
15
+ '@eeacms/search': '<rootDir>/node_modules/@eeacms/volto-searchlib/src',
15
16
  '@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
16
17
  '@plone/volto-slate':
17
18
  '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
@@ -19,6 +20,7 @@ module.exports = {
19
20
  'load-volto-addons':
20
21
  '<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
21
22
  },
23
+ transformIgnorePatterns: ['node_modules/(?!@eeacms)/volto-listing-block'],
22
24
  transform: {
23
25
  '^.+\\.js(x)?$': 'babel-jest',
24
26
  '^.+\\.(png)$': 'jest-file',
@@ -0,0 +1,14 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: \n"
8
+ "Language: \n"
9
+ "Language-Team: \n"
10
+ "Content-Type: \n"
11
+ "Content-Transfer-Encoding: \n"
12
+ "Plural-Forms: \n"
13
+
14
+
@@ -0,0 +1,14 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: \n"
8
+ "Language: \n"
9
+ "Language-Team: \n"
10
+ "Content-Type: \n"
11
+ "Content-Transfer-Encoding: \n"
12
+ "Plural-Forms: \n"
13
+
14
+
@@ -0,0 +1,14 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: \n"
8
+ "Language: \n"
9
+ "Language-Team: \n"
10
+ "Content-Type: \n"
11
+ "Content-Transfer-Encoding: \n"
12
+ "Plural-Forms: \n"
13
+
14
+
package/locales/volto.pot CHANGED
@@ -0,0 +1,16 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Plone\n"
4
+ "POT-Creation-Date: 2023-06-28T10:48:22.678Z\n"
5
+ "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
6
+ "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
7
+ "MIME-Version: 1.0\n"
8
+ "Content-Type: text/plain; charset=utf-8\n"
9
+ "Content-Transfer-Encoding: 8bit\n"
10
+ "Plural-Forms: nplurals=1; plural=0;\n"
11
+ "Language-Code: en\n"
12
+ "Language-Name: English\n"
13
+ "Preferred-Encodings: utf-8\n"
14
+ "Domain: volto\n"
15
+
16
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-globalsearch",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "@eeacms/volto-globalsearch: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import LandingPage from './MasonryLandingPage';
4
+ import { runRequest } from '@eeacms/search';
5
+ import '@testing-library/jest-dom/extend-expect';
6
+
7
+ jest.mock('@eeacms/search', () => ({
8
+ runRequest: jest.fn(),
9
+ }));
10
+
11
+ describe('LandingPage', () => {
12
+ it('renders loading state', () => {
13
+ runRequest.mockReturnValue(new Promise(() => {}));
14
+ const { container } = render(<LandingPage appConfig={{}} />);
15
+ expect(container.innerHTML).toBe('');
16
+ });
17
+
18
+ it('renders based on fetched data', async () => {
19
+ const landingDataAggs = {
20
+ hits: { total: { value: 100 } },
21
+ aggregations: {
22
+ min_timecoverage: { value: 2000 },
23
+ max_timecoverage: { value: 2020 },
24
+ organisations: { buckets: [] },
25
+ topics: { buckets: [] },
26
+ languages: { buckets: [] },
27
+ content_types: { buckets: [] },
28
+ countries: { buckets: [] },
29
+ },
30
+ };
31
+ const landingDataRes = {
32
+ hits: { hits: [] },
33
+ };
34
+
35
+ runRequest
36
+ .mockReturnValueOnce(Promise.resolve({ body: landingDataAggs }))
37
+ .mockReturnValueOnce(Promise.resolve({ body: landingDataRes }));
38
+
39
+ const { getByText } = render(<LandingPage appConfig={{}} />);
40
+
41
+ // Use setTimeout to wait for the promises to resolve
42
+ await new Promise((resolve) => setTimeout(resolve, 0));
43
+
44
+ expect(
45
+ getByText(
46
+ 'Instantly search over 20 years of environmental knowledge by EEA',
47
+ ),
48
+ ).toBeInTheDocument();
49
+ expect(getByText('Documents')).toBeInTheDocument();
50
+ expect(getByText('100')).toBeInTheDocument();
51
+ expect(getByText('Languages')).toBeInTheDocument();
52
+ });
53
+
54
+ it('renders based on fetched data', async () => {
55
+ const landingDataAggs = {
56
+ hits: { total: { value: 100 } },
57
+ aggregations: {
58
+ min_timecoverage: { value: 2000 },
59
+ max_timecoverage: { value: 2020 },
60
+ organisations: { buckets: [] },
61
+ topics: { buckets: [] },
62
+ languages: { buckets: [] },
63
+ content_types: { buckets: [{ key: 'Document' }, { key: 'Image' }] },
64
+ countries: { buckets: [{ key: 'Romania' }, { key: 'Belgium' }] },
65
+ },
66
+ };
67
+ const landingDataRes = {
68
+ hits: {
69
+ hits: [
70
+ {
71
+ _source: {
72
+ title: 'Document 1',
73
+ about: 'test',
74
+ issued: '07/08/2023',
75
+ },
76
+ },
77
+ ],
78
+ },
79
+ };
80
+
81
+ runRequest
82
+ .mockReturnValueOnce(Promise.resolve({ body: landingDataAggs }))
83
+ .mockReturnValueOnce(Promise.resolve({ body: landingDataRes }));
84
+
85
+ const { getByText } = render(<LandingPage appConfig={{}} />);
86
+
87
+ // Use setTimeout to wait for the promises to resolve
88
+ await new Promise((resolve) => setTimeout(resolve, 0));
89
+
90
+ expect(
91
+ getByText(
92
+ 'Instantly search over 20 years of environmental knowledge by EEA',
93
+ ),
94
+ ).toBeInTheDocument();
95
+ expect(getByText('Documents')).toBeInTheDocument();
96
+ expect(getByText('100')).toBeInTheDocument();
97
+ expect(getByText('Languages')).toBeInTheDocument();
98
+ });
99
+ });
@@ -1,23 +1,98 @@
1
1
  import runRequest from '@eeacms/search/lib/runRequest';
2
2
  import buildRequest from '@eeacms/search/lib/search/query';
3
+ import getInfo from '@eeacms/search/lib/getIndexInfo';
3
4
 
4
- export default function healthcheck(appConfig) {
5
+ const default_documentCountThreshold = 60000;
6
+ const default_queryTimeSecondsThreshold_OK = 2;
7
+ const default_queryTimeSecondsThreshold_WARNING = 5;
8
+ const default_indexUpdatedHoursThreshold_OK = 1;
9
+ const default_indexUpdatedHoursThreshold_WARNING = 1;
10
+
11
+ export default async function healthcheck(appConfig, query) {
5
12
  // is index ok?
6
13
  // return index update date
7
14
  // run default query, see number of results
8
15
  // nlpservice provides answer based on extracted term
9
16
  // number of documents with error in data raw, type of error
10
17
 
11
- return new Promise((resolve, reject) => {
12
- const body = buildRequest({ filters: [] }, appConfig);
13
- runRequest(body, appConfig).then((resp) => {
14
- let total;
15
- try {
16
- total = resp.body.hits.total.value;
17
- resolve({ documentCount: total });
18
- } catch {
19
- reject({ total: -1 });
18
+ return new Promise(async (resolve, reject) => {
19
+ try {
20
+ let {
21
+ documentCountThreshold,
22
+ queryTimeSecondsThreshold_OK,
23
+ queryTimeSecondsThreshold_WARNING,
24
+ indexUpdatedHoursThreshold_OK,
25
+ indexUpdatedHoursThreshold_WARNING,
26
+ } = query;
27
+ documentCountThreshold =
28
+ documentCountThreshold || default_documentCountThreshold;
29
+ queryTimeSecondsThreshold_OK =
30
+ queryTimeSecondsThreshold_OK || default_queryTimeSecondsThreshold_OK;
31
+ queryTimeSecondsThreshold_WARNING =
32
+ queryTimeSecondsThreshold_WARNING ||
33
+ default_queryTimeSecondsThreshold_WARNING;
34
+ indexUpdatedHoursThreshold_OK =
35
+ indexUpdatedHoursThreshold_OK || default_indexUpdatedHoursThreshold_OK;
36
+ indexUpdatedHoursThreshold_WARNING =
37
+ indexUpdatedHoursThreshold_WARNING ||
38
+ default_indexUpdatedHoursThreshold_WARNING;
39
+
40
+ const body_total = buildRequest({ filters: [] }, appConfig);
41
+
42
+ const resp_total = await runRequest(body_total, appConfig);
43
+ const total = resp_total.body.hits.total.value;
44
+ const total_status = total > documentCountThreshold ? 'Ok' : 'CRITICAL';
45
+ const body_nlp = buildRequest(
46
+ { filters: [], searchTerm: 'what is bise?' },
47
+ appConfig,
48
+ );
49
+ const resp_nlp = await runRequest(body_nlp, appConfig);
50
+ const elapsed = resp_nlp.body.elapsed;
51
+
52
+ let total_elapsed = 0;
53
+ Object.keys(elapsed).forEach((key) => {
54
+ elapsed[key].forEach((nlp_step) => {
55
+ Object.keys(nlp_step).forEach((step_name) => {
56
+ total_elapsed += nlp_step[step_name].delta;
57
+ });
58
+ });
59
+ });
60
+ const elapsed_status =
61
+ total_elapsed < queryTimeSecondsThreshold_OK
62
+ ? 'OK'
63
+ : total_elapsed < queryTimeSecondsThreshold_WARNING
64
+ ? 'WARNING'
65
+ : 'CRITICAL';
66
+
67
+ const indexedAt = await getInfo(appConfig);
68
+ const currentDate = new Date();
69
+ const indexedDelta = (currentDate - indexedAt) / 36000000;
70
+ const indexed_status =
71
+ indexedDelta < indexUpdatedHoursThreshold_OK
72
+ ? 'OK'
73
+ : indexedDelta < indexUpdatedHoursThreshold_WARNING
74
+ ? 'WARNING'
75
+ : 'CRITICAL';
76
+
77
+ let status = 'OK';
78
+ if (elapsed_status === 'WARNING' || indexed_status === 'WARNING') {
79
+ status = 'WARNING';
80
+ }
81
+ if (
82
+ total_status === 'CRITICAL' ||
83
+ elapsed_status === 'CRITICAL' ||
84
+ indexed_status === 'CRITICAL'
85
+ ) {
86
+ status = 'CRITICAL';
20
87
  }
21
- });
88
+ resolve({
89
+ status: status,
90
+ documentCount: total_status,
91
+ queryTime: elapsed_status,
92
+ indexUpdated: indexed_status,
93
+ });
94
+ } catch {
95
+ reject({ status: 'Critical' });
96
+ }
22
97
  });
23
98
  }
@@ -0,0 +1,175 @@
1
+ import install from './index';
2
+ import '@testing-library/jest-dom/extend-expect';
3
+
4
+ const SLOTS = [
5
+ 'aboveSearchInput',
6
+ 'belowSearchInput',
7
+ 'aboveResults',
8
+ 'belowResults',
9
+ ];
10
+
11
+ jest.mock('@eeacms/search', () => ({
12
+ runRequest: jest.fn(),
13
+ booleanFacet: jest.fn(),
14
+ multiTermFacet: jest.fn(),
15
+ makeRange: jest.fn(),
16
+ histogramFacet: jest.fn(),
17
+ fixedRangeFacet: jest.fn(),
18
+ dateRangeFacet: jest.fn(),
19
+ mergeConfig: jest.fn((config) => config),
20
+ suiFacet: jest.fn(),
21
+ isFilterValueDefaultValue: jest.fn(),
22
+ SLOTS: SLOTS,
23
+ }));
24
+
25
+ jest.mock('./facets', () => ({
26
+ facets: [],
27
+ }));
28
+
29
+ jest.mock('./clusters', () => ({
30
+ typesForClustersOptionsFilter: jest.fn(),
31
+ clusters: {
32
+ name: 'op_cluster',
33
+ field: 'objectProvides',
34
+ clusters: [
35
+ {
36
+ name: 'News',
37
+ icon: { name: 'bullhorn' },
38
+ values: ['News', 'Article'],
39
+ defaultResultView: 'horizontalCard',
40
+ },
41
+ {
42
+ name: 'Publications',
43
+ icon: { name: 'book' },
44
+ values: [
45
+ 'Report',
46
+ 'Indicator',
47
+ 'Briefing',
48
+ 'Topic page',
49
+ 'Country fact sheet',
50
+ ],
51
+ defaultResultView: 'horizontalCard',
52
+ },
53
+ ],
54
+ },
55
+ }));
56
+
57
+ describe('getActiveFilters', () => {
58
+ it('should return an array with issued.date', () => {
59
+ const filters = [
60
+ { field: 'foo' },
61
+ { field: 'issued.date', values: ['All time'] },
62
+ { field: 'issued.date', values: ['test'] },
63
+ ];
64
+ const appConfig = {
65
+ searchui: {
66
+ default: {},
67
+ minimal: {
68
+ facets: [],
69
+ },
70
+ },
71
+ resolve: {
72
+ getGlobalSearchhealthcheck: 'global',
73
+ },
74
+ facets: [
75
+ {
76
+ isFilter: true,
77
+ showInFacetsList: undefined,
78
+ field: 'issued.date',
79
+ },
80
+ ],
81
+ };
82
+ const result = install(appConfig);
83
+ const activeFilters = result.resolve.getGlobalSearchActiveFilters(
84
+ filters,
85
+ appConfig,
86
+ );
87
+ expect(activeFilters).toEqual([{ field: 'issued.date', values: ['test'] }]);
88
+ });
89
+
90
+ it('should return an array with provided field name', () => {
91
+ const filters = [{ field: 'foo' }];
92
+ const appConfig = {
93
+ searchui: {
94
+ default: {},
95
+ minimal: {
96
+ facets: [],
97
+ },
98
+ },
99
+ resolve: {
100
+ getGlobalSearchhealthcheck: 'global',
101
+ },
102
+ facets: [
103
+ {
104
+ isFilter: false,
105
+ showInFacetsList: true,
106
+ field: 'foo',
107
+ },
108
+ ],
109
+ };
110
+ const result = install(appConfig);
111
+ const activeFilters = result.resolve.getGlobalSearchActiveFilters(
112
+ filters,
113
+ appConfig,
114
+ );
115
+ expect(activeFilters).toEqual([
116
+ {
117
+ field: 'foo',
118
+ },
119
+ ]);
120
+ });
121
+
122
+ it('should return an empty array if no facets are provided', () => {
123
+ const filters = [{ field: 'foo' }];
124
+ const appConfig = {
125
+ searchui: {
126
+ default: {},
127
+ minimal: {
128
+ facets: [],
129
+ },
130
+ },
131
+ resolve: {
132
+ getGlobalSearchhealthcheck: 'global',
133
+ },
134
+ facets: undefined,
135
+ };
136
+ const result = install(appConfig);
137
+ const activeFilters = result.resolve.getGlobalSearchActiveFilters(
138
+ filters,
139
+ appConfig,
140
+ );
141
+ expect(activeFilters).toEqual([]);
142
+ });
143
+
144
+ it('should return an array with provided field name and showInFacetsList is undefined', () => {
145
+ const filters = [{ field: 'foo' }];
146
+ const appConfig = {
147
+ searchui: {
148
+ default: {},
149
+ minimal: {
150
+ facets: [],
151
+ },
152
+ },
153
+ resolve: {
154
+ getGlobalSearchhealthcheck: 'global',
155
+ },
156
+ facets: [
157
+ {
158
+ isFilter: false,
159
+ showInFacetsList: undefined,
160
+ field: 'foo',
161
+ },
162
+ ],
163
+ };
164
+ const result = install(appConfig);
165
+ const activeFilters = result.resolve.getGlobalSearchActiveFilters(
166
+ filters,
167
+ appConfig,
168
+ );
169
+ expect(activeFilters).toEqual([
170
+ {
171
+ field: 'foo',
172
+ },
173
+ ]);
174
+ });
175
+ });
@@ -0,0 +1,435 @@
1
+ import {
2
+ build_runtime_mappings,
3
+ getTodayWithTime,
4
+ getGlobalsearchIconUrl,
5
+ getGlobalsearchThumbUrl,
6
+ get_cluster_icons,
7
+ get_cluster_icons_dict,
8
+ } from './utils';
9
+ import '@testing-library/jest-dom/extend-expect';
10
+
11
+ describe('build_runtime_mappings', () => {
12
+ it('should build runtime mappings based on settings', () => {
13
+ const settings = {
14
+ clusters: [
15
+ { name: 'cluster1', values: ['value1', 'value2'] },
16
+ { name: 'cluster2', values: ['value3', 'value4'] },
17
+ ],
18
+ field: 'fieldName',
19
+ name: 'mappingName',
20
+ };
21
+
22
+ const expectedMappings = {
23
+ mappingName: {
24
+ type: 'keyword',
25
+ script: {
26
+ source:
27
+ 'emit("_all_"); def clusters_settings = [["name": "cluster1", "values": ["value1","value2"]],["name": "cluster2", "values": ["value3","value4"]]]; def vals = doc[\'fieldName\']; def clusters = [\'All\']; for (val in vals) { for (cs in clusters_settings) { if (cs.values.contains(val)) { emit(cs.name) } } }',
28
+ },
29
+ },
30
+ };
31
+
32
+ const mappings = build_runtime_mappings(settings);
33
+
34
+ expect(mappings).toEqual(expectedMappings);
35
+ });
36
+ });
37
+
38
+ describe('getTodayWithTime', () => {
39
+ it('should return the current date in UTC format', () => {
40
+ // The output will vary depending on the current date and time,
41
+ // so we can't hardcode the expected value. We will only test
42
+ // if the function returns a string in a valid UTC format.
43
+ const output = getTodayWithTime();
44
+
45
+ expect(typeof output).toBe('string');
46
+ expect(output).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/);
47
+ });
48
+ });
49
+
50
+ describe('getGlobalsearchIconUrl', () => {
51
+ const contentTypeNormalize = {
52
+ content_type_1: 'icon_type_1',
53
+ content_type_2: 'icon_type_2',
54
+ };
55
+
56
+ it('should return the default fallback image when no match is found', () => {
57
+ const result = {
58
+ about: { raw: 'some_non_matching_url' },
59
+ objectProvides: { raw: 'Non-matching Content Type' },
60
+ };
61
+
62
+ const config = null;
63
+ const fallback =
64
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_thumb';
65
+
66
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
67
+ result,
68
+ config,
69
+ fallback,
70
+ );
71
+
72
+ expect(imageUrl).toBe(fallback);
73
+ });
74
+
75
+ it('should return the glossary term icon for matching about.raw URLs', () => {
76
+ const result = {
77
+ about: { raw: 'http://www.eea.europa.eu/help/glossary/some_term' },
78
+ };
79
+
80
+ const config = null;
81
+ const fallback = 'fallback_image_url';
82
+
83
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
84
+ result,
85
+ config,
86
+ fallback,
87
+ );
88
+
89
+ expect(imageUrl).toBe(
90
+ 'https://www.eea.europa.eu/portal_depiction/term/image_thumb',
91
+ );
92
+ });
93
+
94
+ it('should return the content_type_1 e icon for matching objectProvides.raw', () => {
95
+ const result = {
96
+ about: { raw: 'some_non_matching_url' },
97
+ objectProvides: { raw: 'content_type_1' },
98
+ };
99
+
100
+ const config = null;
101
+ const fallback = 'fallback_image_url';
102
+
103
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
104
+ result,
105
+ config,
106
+ fallback,
107
+ );
108
+
109
+ expect(imageUrl).toBe(
110
+ 'https://www.eea.europa.eu/portal_depiction/icon_type_1/image_thumb',
111
+ );
112
+ });
113
+
114
+ it('should return the country profile icon for matching objectProvides.raw', () => {
115
+ const result = {
116
+ about: { raw: 'some_non_matching_url' },
117
+ objectProvides: { raw: 'Country profile' },
118
+ };
119
+
120
+ const config = null;
121
+ const fallback = 'fallback_image_url';
122
+
123
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
124
+ result,
125
+ config,
126
+ fallback,
127
+ );
128
+
129
+ expect(imageUrl).toBe(
130
+ 'https://www.eea.europa.eu/portal_depiction/country-profile/image_thumb',
131
+ );
132
+ });
133
+
134
+ it('should return the generic if no content_type matches the ones in contentTypeNormalize', () => {
135
+ const result = {
136
+ about: { raw: 'some_non_matching_url' },
137
+ objectProvides: { raw: ['content_type_3', 'content_type_4'] },
138
+ };
139
+
140
+ const config = null;
141
+ const fallback = 'fallback_image_url';
142
+
143
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
144
+ result,
145
+ config,
146
+ fallback,
147
+ );
148
+
149
+ expect(imageUrl).toBe(
150
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_thumb',
151
+ );
152
+ });
153
+
154
+ it('should return the generic if objectProvides.raw is empty', () => {
155
+ const result = {
156
+ about: { raw: 'some_non_matching_url' },
157
+ objectProvides: { raw: [] },
158
+ };
159
+
160
+ const config = null;
161
+ const fallback = 'fallback_image_url';
162
+
163
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
164
+ result,
165
+ config,
166
+ fallback,
167
+ );
168
+
169
+ expect(imageUrl).toBe(
170
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_thumb',
171
+ );
172
+ });
173
+
174
+ it('should return the data icon for matching about.raw URL containing land.copernicus.eu', () => {
175
+ const result = {
176
+ about: { raw: 'https://land.copernicus.eu/some_data' },
177
+ };
178
+
179
+ const config = null;
180
+ const fallback = 'fallback_image_url';
181
+
182
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
183
+ result,
184
+ config,
185
+ fallback,
186
+ );
187
+
188
+ expect(imageUrl).toBe(
189
+ 'https://www.eea.europa.eu/portal_depiction/data/image_thumb',
190
+ );
191
+ });
192
+
193
+ it('should return the image_preview URL from result if specific conditions are met', () => {
194
+ const result = {
195
+ site_id: { raw: 'sdi' },
196
+ 'overview.url': { raw: 'some_overview_url' },
197
+ about: { raw: 'some_non_matching_url' },
198
+ };
199
+
200
+ const config = null;
201
+ const fallback = 'fallback_image_url';
202
+
203
+ const imageUrl = getGlobalsearchIconUrl(contentTypeNormalize)(
204
+ result,
205
+ config,
206
+ fallback,
207
+ );
208
+
209
+ expect(imageUrl).toBe('fallback_image_url');
210
+ });
211
+ });
212
+
213
+ describe('getGlobalsearchThumbUrl', () => {
214
+ const contentTypeNormalize = {
215
+ content_type_1: 'icon_type_1',
216
+ content_type_2: 'icon_type_2',
217
+ };
218
+
219
+ it('should return the default fallback image when no match is found', () => {
220
+ const result = {
221
+ about: { raw: 'some_non_matching_url' },
222
+ objectProvides: { raw: 'Non-matching Content Type' },
223
+ };
224
+
225
+ const config = null;
226
+ const fallback =
227
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_preview';
228
+
229
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
230
+ result,
231
+ config,
232
+ fallback,
233
+ );
234
+
235
+ expect(imageUrl).toBe(fallback);
236
+ });
237
+
238
+ it('should return the glossary term preview image for matching about.raw URLs', () => {
239
+ const result = {
240
+ about: { raw: 'http://www.eea.europa.eu/help/glossary/some_term' },
241
+ };
242
+
243
+ const config = null;
244
+ const fallback =
245
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_preview';
246
+
247
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
248
+ result,
249
+ config,
250
+ fallback,
251
+ );
252
+
253
+ expect(imageUrl).toBe(
254
+ 'http://www.eea.europa.eu/help/glossary/some_term/image_preview',
255
+ );
256
+ });
257
+
258
+ it('should return the objectProvides.raw icon for matching objectProvides.raw', () => {
259
+ const result = {
260
+ about: { raw: 'some_non_matching_url' },
261
+ objectProvides: { raw: 'content_type_1' },
262
+ };
263
+
264
+ const config = null;
265
+ const fallback =
266
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_preview';
267
+
268
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
269
+ result,
270
+ config,
271
+ fallback,
272
+ );
273
+
274
+ expect(imageUrl).toBe(
275
+ 'https://www.eea.europa.eu/portal_depiction/icon_type_1/image_preview',
276
+ );
277
+ });
278
+
279
+ it('should return the generic for not matching objectProvides.raw', () => {
280
+ const result = {
281
+ about: { raw: 'some_non_matching_url' },
282
+ objectProvides: { raw: ['content_type_3', 'content_type_4'] },
283
+ };
284
+
285
+ const config = null;
286
+ const fallback =
287
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_preview';
288
+
289
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
290
+ result,
291
+ config,
292
+ fallback,
293
+ );
294
+
295
+ expect(imageUrl).toBe(fallback);
296
+ });
297
+
298
+ it('should return generic for empty objectProvides.raw', () => {
299
+ const result = {
300
+ about: { raw: 'some_non_matching_url' },
301
+ objectProvides: { raw: [] },
302
+ };
303
+
304
+ const config = null;
305
+ const fallback =
306
+ 'https://www.eea.europa.eu/portal_depiction/generic/image_preview';
307
+
308
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
309
+ result,
310
+ config,
311
+ fallback,
312
+ );
313
+
314
+ expect(imageUrl).toBe(fallback);
315
+ });
316
+
317
+ it('should return the glossary term preview image for matching about.raw URLs', () => {
318
+ const result = {
319
+ about: { raw: 'http://www.eea.europa.eu/en/help/glossary/some_term' },
320
+ image_preview: undefined,
321
+ };
322
+
323
+ const config = null;
324
+ const fallback =
325
+ 'https://www.eea.europa.eu/portal_depiction/country-profile/image_preview';
326
+
327
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
328
+ result,
329
+ config,
330
+ fallback,
331
+ );
332
+
333
+ expect(imageUrl).toBe(fallback);
334
+ });
335
+
336
+ it('should return the data preview image for matching about.raw URL containing land.copernicus.eu', () => {
337
+ const result = {
338
+ about: { raw: 'https://land.copernicus.eu/some_data' },
339
+ };
340
+
341
+ const config = null;
342
+ const fallback = 'fallback_image_url';
343
+
344
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
345
+ result,
346
+ config,
347
+ fallback,
348
+ );
349
+
350
+ expect(imageUrl).toBe('https://land.copernicus.eu/some_data/image_preview');
351
+ });
352
+
353
+ it('should return the specific preview image from result if specific conditions are met', () => {
354
+ const result = {
355
+ site_id: { raw: 'sdi' },
356
+ 'overview.url': { raw: 'some_overview_url' },
357
+ image_preview: { raw: 'specific_preview_image' },
358
+ };
359
+
360
+ const config = null;
361
+ const fallback = 'fallback_image_url';
362
+
363
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
364
+ result,
365
+ config,
366
+ fallback,
367
+ );
368
+
369
+ expect(imageUrl).toBe('specific_preview_image');
370
+ });
371
+
372
+ it('should return the image_preview URL from result for specific about.raw URLs', () => {
373
+ const result = {
374
+ about: { raw: 'https://biodiversity.europa.eu/some_data' },
375
+ image_preview: { raw: 'specific_about_image_preview' },
376
+ };
377
+
378
+ const config = null;
379
+ const fallback = 'fallback_image_url';
380
+
381
+ const imageUrl = getGlobalsearchThumbUrl(contentTypeNormalize)(
382
+ result,
383
+ config,
384
+ fallback,
385
+ );
386
+
387
+ expect(imageUrl).toBe('specific_about_image_preview');
388
+ });
389
+ });
390
+
391
+ describe('get_cluster_icons', () => {
392
+ it('should return icons object from clusters with multiple values', () => {
393
+ const settings = {
394
+ clusters: [
395
+ { name: 'cluster1', icon: 'icon1', values: ['value1', 'value2'] },
396
+ { name: 'cluster2', icon: 'icon2', values: ['value3'] },
397
+ ],
398
+ };
399
+
400
+ const expectedIcons = {
401
+ fallback: { name: 'file outline' },
402
+ value1: { cluster: 'cluster1', icon: 'icon1' },
403
+ value2: { cluster: 'cluster1', icon: 'icon1' },
404
+ value3: { cluster: 'cluster2', icon: 'icon2' },
405
+ };
406
+
407
+ const icons = get_cluster_icons(settings);
408
+
409
+ expect(icons).toEqual(expectedIcons);
410
+ });
411
+ });
412
+
413
+ describe('get_cluster_icons_dict', () => {
414
+ it('should return icons object from clusters with multiple values', () => {
415
+ const settings = {
416
+ clusters: [
417
+ { name: 'cluster1', icon: 'icon1', values: ['value1', 'value2'] },
418
+ { name: 'cluster2', icon: 'icon2', values: ['value3'] },
419
+ ],
420
+ };
421
+
422
+ const expectedIcons = {
423
+ fallback: { name: 'file outline' },
424
+ value1: 'icon1',
425
+ value2: 'icon1',
426
+ value3: 'icon2',
427
+ cluster1: 'icon1',
428
+ cluster2: 'icon2',
429
+ };
430
+
431
+ const icons = get_cluster_icons_dict(settings);
432
+
433
+ expect(icons).toEqual(expectedIcons);
434
+ });
435
+ });
@@ -1 +0,0 @@
1
- module.exports = require('@plone/volto/babel');