Skip to content

Commit d8589ef

Browse files
committed
test(core): tests pour computeSimilarityScore()
1 parent ef70733 commit d8589ef

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
import { JSDOM } from "jsdom";
2+
3+
let computeSimilarityScore;
4+
5+
describe("computeSimilarityScore", () => {
6+
let chatbot;
7+
8+
beforeEach(async () => {
9+
const { window } = new JSDOM(`<!DOCTYPE html><body>
10+
<h1 id="chatbot-name">&nbsp;</h1>
11+
<main>
12+
<div id="chat" class="chat-container" role="region" aria-label="Zone de conversation">
13+
<!-- La conversation sera affichée ici -->
14+
</div>
15+
<div id="controls">
16+
<div id="input-container">
17+
<label id="user-input-label" class="sr-only" for="user-input">Écrivez votre message</label>
18+
<div id="user-input" contenteditable="true" placeholder="Écrivez votre message" tabindex="0" role="textbox" aria-labelledby="user-input-label" title="Écrivez votre message"></div>
19+
</div>
20+
<button id="send-button" type="button">Envoyer</button>
21+
</div>
22+
</main>
23+
<footer id="footer">
24+
ChatMD – Outil libre & gratuit créé par <a href="https://eyssette.forge.apps.education.fr/">Cédric Eyssette</a>
25+
</footer>
26+
<script src="script.min.js"></script>
27+
</body>`);
28+
29+
global.window = window;
30+
global.document = window.document;
31+
32+
// Importer le module APRÈS avoir créé global.document
33+
const mod = await import(
34+
"../../../../../../app/js/core/interactions/helpers/findBestResponse/computeSimilarityScore.mjs"
35+
);
36+
computeSimilarityScore = mod.computeSimilarityScore;
37+
38+
chatbot = {
39+
responses: [],
40+
nextMessage: {
41+
needsProcessing: false,
42+
goto: null,
43+
selected: null,
44+
ignoreKeywords: false,
45+
},
46+
vectorChatBotResponses: [],
47+
};
48+
});
49+
50+
it("returns no match when there are no responses", () => {
51+
const result = computeSimilarityScore(chatbot, "contenu pour tester");
52+
53+
expect(result.bestMatch).toBe(null);
54+
expect(result.bestMatchScore).toBe(0);
55+
expect(result.indexBestMatch).toBeUndefined();
56+
});
57+
58+
it("returns no match when responses have no titles", () => {
59+
chatbot.responses = [{ content: "Réponse sans titre", title: "" }];
60+
const result = computeSimilarityScore(chatbot, "contenu pour tester");
61+
62+
expect(result.bestMatch).toBe(null);
63+
expect(result.bestMatchScore).toBe(0);
64+
});
65+
66+
it("finds the best matching response based on keywords", () => {
67+
chatbot.responses = [
68+
{
69+
title: "Horaires",
70+
keywords: ["horaire", "ouverture", "fermé"],
71+
content: "Nous sommes ouverts de 9h à 18h",
72+
},
73+
{
74+
title: "Contact",
75+
keywords: ["téléphone", "appeler"],
76+
content: "Voici notre numéro de téléphone : xx-xx-xx-xx-xx",
77+
},
78+
];
79+
const result = computeSimilarityScore(
80+
chatbot,
81+
"Comment faire pour vous appeler ?",
82+
);
83+
84+
expect(result.bestMatch).toBe(
85+
"Voici notre numéro de téléphone : xx-xx-xx-xx-xx",
86+
);
87+
expect(result.bestMatchScore).toBeGreaterThan(0);
88+
expect(result.indexBestMatch).toBe(1);
89+
});
90+
91+
it("gives higher score for longer matching keywords", () => {
92+
chatbot.responses = [
93+
{
94+
title: "Test1",
95+
keywords: ["info"],
96+
content: "Réponse courte",
97+
},
98+
{
99+
title: "Test2",
100+
keywords: ["information"],
101+
content: "Réponse longue",
102+
},
103+
{
104+
title: "Test3",
105+
keywords: ["informationnel"],
106+
content: "Réponse encore plus longue",
107+
},
108+
];
109+
110+
const result = computeSimilarityScore(chatbot, "information");
111+
112+
expect(result.indexBestMatch).toBe(1);
113+
});
114+
115+
it("handles accents in keywords and user input", () => {
116+
chatbot.responses = [
117+
{
118+
title: "Info",
119+
keywords: ["café"],
120+
content: "Réponse café",
121+
},
122+
];
123+
124+
const result = computeSimilarityScore(chatbot, "cafe");
125+
126+
expect(result.bestMatchScore).toBeGreaterThan(0);
127+
});
128+
129+
it("includes title in matching when no keywords", () => {
130+
chatbot.responses = [
131+
{
132+
title: "Horaires",
133+
keywords: [],
134+
content: "Les horaires sont affichés",
135+
},
136+
];
137+
138+
const result = computeSimilarityScore(chatbot, "horaires");
139+
140+
expect(result.bestMatchScore).toBe(30.8);
141+
});
142+
143+
it("penalizes responses with negative keywords present in user input", () => {
144+
chatbot.responses = [
145+
{
146+
title: "Réponse1",
147+
keywords: ["! non merci", "aide", "aidant", "aidez-moi"],
148+
content: "Première réponse",
149+
},
150+
{
151+
title: "Réponse2",
152+
keywords: ["aide"],
153+
content: "Deuxième réponse",
154+
},
155+
];
156+
157+
const result = computeSimilarityScore(chatbot, "aide non merci");
158+
159+
expect(result.indexBestMatch).toBe(1);
160+
});
161+
162+
it("goes to the specified response with !Next", () => {
163+
chatbot.responses = [
164+
{
165+
title: "Etape1",
166+
keywords: [],
167+
content: "Première étape",
168+
},
169+
{
170+
title: "Etape2",
171+
keywords: [],
172+
content: "Deuxième étape",
173+
},
174+
];
175+
chatbot.nextMessage.needsProcessing = true;
176+
chatbot.nextMessage.goto = "Etape2";
177+
178+
const result = computeSimilarityScore(chatbot, "contenu pour tester");
179+
180+
expect(result.indexBestMatch).toBe(1);
181+
});
182+
183+
it("prioritizes the !Next response even if keywords match another response better", () => {
184+
chatbot.responses = [
185+
{
186+
title: "Autre",
187+
keywords: ["argument", "argumentation", "argumenter", "arguments"],
188+
content: "Autre réponse",
189+
},
190+
{
191+
title: "Cible",
192+
keywords: ["argument"],
193+
content: "Réponse ciblée",
194+
},
195+
];
196+
chatbot.nextMessage.needsProcessing = true;
197+
chatbot.nextMessage.goto = "Cible";
198+
199+
const result = computeSimilarityScore(chatbot, "argument");
200+
201+
expect(result.indexBestMatch).toBe(1);
202+
});
203+
204+
it("takes keywords into account for !Next if ignoreKeywords is set to false", () => {
205+
chatbot.responses = [
206+
{
207+
title: "Cible",
208+
keywords: ["specifique"],
209+
content: "Réponse avec keyword",
210+
},
211+
];
212+
chatbot.nextMessage.needsProcessing = true;
213+
chatbot.nextMessage.goto = "Cible";
214+
chatbot.nextMessage.ignoreKeywords = false;
215+
216+
const resultWithKeyword = computeSimilarityScore(chatbot, "specifique");
217+
const resultWithoutKeyword = computeSimilarityScore(chatbot, "autre");
218+
219+
expect(resultWithKeyword.bestMatchScore).toBeGreaterThan(
220+
resultWithoutKeyword.bestMatchScore,
221+
);
222+
expect(resultWithKeyword.indexBestMatch).toBe(0);
223+
expect(resultWithoutKeyword.indexBestMatch).not.toBe(0);
224+
});
225+
226+
it("gives a higher score for exact match and a lower score for partial match in !Next mode", () => {
227+
chatbot.responses = [
228+
{
229+
title: "contenu pour tester",
230+
keywords: ["contenu pour tester"],
231+
content: "Réponse test",
232+
},
233+
];
234+
chatbot.nextMessage.needsProcessing = true;
235+
chatbot.nextMessage.goto = "contenu pour tester";
236+
237+
const resultExact = computeSimilarityScore(chatbot, "contenu pour tester");
238+
const resultPartiel = computeSimilarityScore(chatbot, "testabc");
239+
240+
expect(resultExact.bestMatchScore).toBeGreaterThan(
241+
resultPartiel.bestMatchScore,
242+
);
243+
});
244+
245+
it("checks only the selected response with !SelectNext", () => {
246+
chatbot.responses = [
247+
{
248+
title: "Option1",
249+
keywords: ["test", "tests", "tester", "testons"],
250+
content: "Première option",
251+
},
252+
{
253+
title: "Option2",
254+
keywords: ["test"],
255+
content: "Deuxième option",
256+
},
257+
];
258+
chatbot.nextMessage.selected = "Option2";
259+
260+
const result = computeSimilarityScore(chatbot, "testons");
261+
262+
expect(result.indexBestMatch).toBe(1);
263+
});
264+
265+
it("finds approximate matches with minor typos", () => {
266+
chatbot.responses = [
267+
{
268+
title: "contenu pour tester",
269+
keywords: ["bonjour"],
270+
content: "Réponse bonjour",
271+
},
272+
];
273+
274+
const result = computeSimilarityScore(chatbot, "bonjur");
275+
276+
expect(result.bestMatchScore).toBeGreaterThan(0);
277+
});
278+
279+
it("ignores or penalizes greatly short keywords in user input", () => {
280+
chatbot.responses = [
281+
{
282+
title: "contenu pour tester",
283+
keywords: ["bonjour", "poteau"],
284+
content: "Réponse",
285+
},
286+
];
287+
288+
const result = computeSimilarityScore(chatbot, "bon pote");
289+
290+
expect(result.bestMatchScore).toBeLessThan(5);
291+
});
292+
293+
it("does not ignore short keywords", () => {
294+
chatbot.responses = [
295+
{
296+
title: "contenu pour tester",
297+
keywords: ["art"],
298+
content: "Réponse courte",
299+
},
300+
];
301+
302+
const result = computeSimilarityScore(
303+
chatbot,
304+
"j'aime ce qui est artistique",
305+
);
306+
307+
expect(result.bestMatchScore).toBe(30.3);
308+
});
309+
310+
it("matches keywords regardless of case", () => {
311+
chatbot.responses = [
312+
{
313+
title: "contenu pour tester",
314+
keywords: ["BOnJoUR"],
315+
content: "Salut",
316+
},
317+
];
318+
319+
const result = computeSimilarityScore(chatbot, "bonjour");
320+
321+
expect(result.bestMatchScore).toBeGreaterThan(0);
322+
});
323+
324+
it("selects the response with the highest bestMatchScore", () => {
325+
chatbot.responses = [
326+
{
327+
title: "Faible",
328+
keywords: ["correspondance", "moins", "importante"],
329+
content: "Réponse faible",
330+
},
331+
{
332+
title: "Forte",
333+
keywords: ["correspondance", "plus", "importante"],
334+
content: "Réponse forte",
335+
},
336+
];
337+
338+
const result = computeSimilarityScore(
339+
chatbot,
340+
"correspondance plus importante",
341+
);
342+
343+
expect(result.indexBestMatch).toBe(1);
344+
expect(result.bestMatch).toBe("Réponse forte");
345+
});
346+
});

0 commit comments

Comments
 (0)