83 lines
3.6 KiB
JavaScript
83 lines
3.6 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { rankRecipeSuggestions } from '#tofu/tools/text/rankRecipeSuggestions.js'
|
|
|
|
// A plausible household library, so the reference case proves a ranking and not a lucky draw.
|
|
const RECIPES = [
|
|
{ id: 'gratin-dauphinois', name: 'Gratin dauphinois', ingredientNames: ['pommes de terre', 'creme', 'ail'] },
|
|
{ id: 'poulet-roti', name: 'Poulet rôti', ingredientNames: ['poulet', 'thym'] },
|
|
{ id: 'salade-de-lentilles', name: 'Salade de lentilles', ingredientNames: ['lentilles', 'echalote'] },
|
|
{ id: 'gnocchis-a-la-sauge', name: 'Gnocchis à la sauge', ingredientNames: ['gnocchis', 'beurre', 'sauge', 'parmesan'] },
|
|
{ id: 'curry-de-pois-chiches', name: 'Curry de pois chiches', ingredientNames: ['pois chiches', 'lait de coco'] },
|
|
{ id: 'crepes', name: 'Crêpes', ingredientNames: ['farine', 'oeufs', 'lait'] },
|
|
{ id: 'soupe-de-potiron', name: 'Soupe de potiron', ingredientNames: ['potiron', 'oignon'] }
|
|
]
|
|
|
|
function idsFor(query, limit) {
|
|
return rankRecipeSuggestions(query, RECIPES, limit).map((recipe) => recipe.id)
|
|
}
|
|
|
|
test('brings "Gnocchis à la sauge" first for the misspelled query "knoci"', () => {
|
|
const ids = idsFor('knoci')
|
|
assert.equal(ids[0], 'gnocchis-a-la-sauge', `got ${JSON.stringify(ids)}`)
|
|
})
|
|
|
|
test('ignores the accents of the recipe name', () => {
|
|
assert.equal(idsFor('crepes')[0], 'crepes')
|
|
assert.equal(idsFor('poulet roti')[0], 'poulet-roti')
|
|
})
|
|
|
|
test('ignores the case of the query', () => {
|
|
assert.equal(idsFor('GNOCCHIS')[0], 'gnocchis-a-la-sauge')
|
|
assert.equal(idsFor('GnOcChIs À La SaUgE')[0], 'gnocchis-a-la-sauge')
|
|
})
|
|
|
|
test('still matches when the query is longer than the recipe name', () => {
|
|
assert.equal(idsFor('gnocchis a la sauge maison')[0], 'gnocchis-a-la-sauge')
|
|
})
|
|
|
|
test('returns nothing for an empty or blank query', () => {
|
|
assert.deepEqual(rankRecipeSuggestions('', RECIPES), [])
|
|
assert.deepEqual(rankRecipeSuggestions(' ', RECIPES), [])
|
|
assert.deepEqual(rankRecipeSuggestions(null, RECIPES), [])
|
|
})
|
|
|
|
test('returns nothing when no recipe reaches the threshold', () => {
|
|
assert.deepEqual(rankRecipeSuggestions('zzzqwx', RECIPES), [])
|
|
})
|
|
|
|
test('returns nothing when there is no recipe at all', () => {
|
|
assert.deepEqual(rankRecipeSuggestions('gnocchis', []), [])
|
|
assert.deepEqual(rankRecipeSuggestions('gnocchis', null), [])
|
|
})
|
|
|
|
test('sorts equal scores by name and honours the limit', () => {
|
|
assert.deepEqual(idsFor('de'), ['curry-de-pois-chiches', 'salade-de-lentilles', 'soupe-de-potiron'])
|
|
assert.deepEqual(idsFor('de', 2), ['curry-de-pois-chiches', 'salade-de-lentilles'])
|
|
assert.deepEqual(idsFor('de', 0), [])
|
|
})
|
|
|
|
test('returns the recipe objects themselves, untouched', () => {
|
|
const [first] = rankRecipeSuggestions('gnocchis', RECIPES)
|
|
assert.equal(first, RECIPES[3])
|
|
})
|
|
|
|
test('a literal match hides the approximate ones', () => {
|
|
// « gratin » is spelled out in « Gratin dauphinois »; « Ratatouille » only
|
|
// shares a couple of bigrams and would be noise next to it.
|
|
const recipes = [
|
|
{ name: 'Gratin dauphinois' },
|
|
{ name: 'Ratatouille' },
|
|
{ name: 'Quiche lorraine' },
|
|
{ name: 'Poulet rôti aux herbes' },
|
|
]
|
|
const suggestions = rankRecipeSuggestions('gratin', recipes)
|
|
assert.deepEqual(suggestions.map((recipe) => recipe.name), ['Gratin dauphinois'])
|
|
})
|
|
|
|
test('a misspelled query still gets its approximate alternatives', () => {
|
|
const recipes = [{ name: 'Gnocchis à la sauge' }, { name: 'Gratin dauphinois' }]
|
|
const suggestions = rankRecipeSuggestions('knoci', recipes)
|
|
assert.equal(suggestions[0].name, 'Gnocchis à la sauge')
|
|
assert.ok(suggestions.length >= 1, 'an approximate query keeps offering candidates')
|
|
})
|