fix(borne): affiche la Grande en menu Maxi (accompagnement) #90
10 changed files with 149 additions and 31 deletions
|
|
@ -75,10 +75,16 @@ final class ProductRepository
|
|||
*/
|
||||
public function availableForCatalogue(): array
|
||||
{
|
||||
// mv.name (LEFT JOIN sur la variante Maxi) : la borne affiche ce nom quand le
|
||||
// menu est commande en Maxi, sans refaire un aller-retour pour resoudre la
|
||||
// variante. NULL si le produit n'a pas de variante Maxi. La SUBSTITUTION reelle
|
||||
// a la commande reste serveur (OrderRepository::resolveSelections) ; ici c'est
|
||||
// un libelle d'affichage seulement.
|
||||
return $this->db->fetchAll(
|
||||
'SELECT p.id, p.category_id, p.name, p.description, p.price_cents, p.size_cl, '
|
||||
. 'p.image_path, p.display_order '
|
||||
. 'p.image_path, p.display_order, mv.name AS maxi_variant_name '
|
||||
. 'FROM product p JOIN category c ON c.id = p.category_id '
|
||||
. 'LEFT JOIN product mv ON mv.id = p.maxi_variant_product_id '
|
||||
. 'WHERE p.is_available = 1 AND c.is_active = 1 AND p.base_product_id IS NULL '
|
||||
. 'ORDER BY p.display_order, p.name',
|
||||
);
|
||||
|
|
@ -158,10 +164,13 @@ final class ProductRepository
|
|||
*/
|
||||
public function findForCatalogue(int $id): ?array
|
||||
{
|
||||
// Meme projection (et meme LEFT JOIN variante Maxi) que la liste : la borne
|
||||
// recoit maxi_variant_name aussi par lien direct (NULL si pas de variante).
|
||||
return $this->db->fetch(
|
||||
'SELECT p.id, p.category_id, p.name, p.description, p.price_cents, '
|
||||
. 'p.image_path, p.display_order '
|
||||
. 'p.image_path, p.display_order, mv.name AS maxi_variant_name '
|
||||
. 'FROM product p JOIN category c ON c.id = p.category_id '
|
||||
. 'LEFT JOIN product mv ON mv.id = p.maxi_variant_product_id '
|
||||
. 'WHERE p.id = :id AND p.is_available = 1 AND c.is_active = 1 AND p.base_product_id IS NULL',
|
||||
['id' => $id],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ class CatalogueController extends Controller
|
|||
* variantes ; vide si le produit n'a pas de dimension taille. Chaque entree
|
||||
* devient {product_id, size_cl, price_cents, label} ; le label humain est
|
||||
* derive du volume ("30 cl") -- aucun slug/enum ne fuit a l'ecran.
|
||||
* @return array{id: int, category_id: int, name: string, description: ?string, price_cents: int, image_path: ?string, display_order: int, sizes: list<array{product_id: int, size_cl: int, price_cents: int, label: string}>}
|
||||
* @return array{id: int, category_id: int, name: string, description: ?string, price_cents: int, image_path: ?string, display_order: int, maxi_variant_name: ?string, sizes: list<array{product_id: int, size_cl: int, price_cents: int, label: string}>}
|
||||
*/
|
||||
private function presentProduct(array $row, array $sizes = []): array
|
||||
{
|
||||
|
|
@ -212,6 +212,10 @@ class CatalogueController extends Controller
|
|||
'price_cents' => (int) ($row['price_cents'] ?? 0),
|
||||
'image_path' => $this->nullableString($row['image_path'] ?? null),
|
||||
'display_order' => (int) ($row['display_order'] ?? 0),
|
||||
// Nom de la variante Maxi de l'accompagnement (ex. "Grande Frite") ; NULL si
|
||||
// le produit n'a pas de variante. La borne l'affiche en format Maxi pour ne
|
||||
// pas montrer "Moyenne Frite" sur un menu agrandi.
|
||||
'maxi_variant_name' => $this->nullableString($row['maxi_variant_name'] ?? null),
|
||||
'sizes' => array_map(
|
||||
static function (array $size): array {
|
||||
$cl = (int) ($size['size_cl'] ?? 0);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ export function loadProducts() {
|
|||
// borne ne montre un picker que si sizes a plus d'une entree.
|
||||
bySlug[slug].push({
|
||||
id: p.id, nom: p.name, prix: p.price_cents, image: p.image_path, type: 'produit',
|
||||
// maxiNom : nom de la variante Maxi (ex. "Grande Frite") quand le produit
|
||||
// en a une, sinon null. Le composeur de menu l'affiche en format Maxi.
|
||||
maxiNom: p.maxi_variant_name ?? null,
|
||||
sizes: Array.isArray(p.sizes) ? p.sizes : [],
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,14 @@ export function compositionLabels(c) {
|
|||
: '';
|
||||
out.push(`${c.burger.libelle}${opts}`);
|
||||
}
|
||||
// libelle fait foi : en Maxi l'accompagnement porte deja sa variante par nom
|
||||
// ("Grande Frite"). Plus de suffixe " grande" -- il doublait le nom ("Grande Frite
|
||||
// grande") et mentait pour la boisson (le menu Maxi ne l'agrandit pas).
|
||||
if (c.accompagnement) {
|
||||
out.push(`${c.accompagnement.libelle}${c.accompagnement.taille === 'G' ? ' grande' : ''}`);
|
||||
out.push(c.accompagnement.libelle);
|
||||
}
|
||||
if (c.boisson) {
|
||||
out.push(`${c.boisson.libelle}${c.boisson.taille === 'G' ? ' grande' : ''}`);
|
||||
out.push(c.boisson.libelle);
|
||||
}
|
||||
if (c.sauce) {
|
||||
out.push(c.sauce.libelle);
|
||||
|
|
|
|||
|
|
@ -141,8 +141,9 @@ function renderCart() {
|
|||
|
||||
/**
|
||||
* Builds the composition breakdown HTML for a menu cart line.
|
||||
* Renders burger (with personalisation options), accompagnement with taille,
|
||||
* boisson with taille, sauce, and the supplement summary if applicable.
|
||||
* Renders burger (with personalisation options), accompagnement, boisson, sauce,
|
||||
* and the supplement summary if applicable. Le format Maxi se lit dans le libelle de
|
||||
* l'accompagnement (variante "Grande ...") et la ligne de supplement, pas un suffixe.
|
||||
*
|
||||
* @param {Object} item — cart item with type === 'menu' and composition object
|
||||
* @returns {string} HTML string
|
||||
|
|
@ -160,11 +161,14 @@ function renderCompositionBlock(item) {
|
|||
: '';
|
||||
parts.push(`${escHtml(c.burger.libelle)}${burgerOpts}`);
|
||||
}
|
||||
// libelle fait foi : en Maxi l'accompagnement porte deja sa variante par nom
|
||||
// ("Grande Frite"). Plus de suffixe taille -- il doublait le nom ("Grande Frite
|
||||
// grande") et "normale"/"grande" mentait pour la boisson (le Maxi ne l'agrandit pas).
|
||||
if (c.accompagnement) {
|
||||
parts.push(`${escHtml(c.accompagnement.libelle)}${c.accompagnement.taille === 'G' ? ' grande' : ' normale'}`);
|
||||
parts.push(escHtml(c.accompagnement.libelle));
|
||||
}
|
||||
if (c.boisson) {
|
||||
parts.push(`${escHtml(c.boisson.libelle)}${c.boisson.taille === 'G' ? ' grande' : ' normale'}`);
|
||||
parts.push(escHtml(c.boisson.libelle));
|
||||
}
|
||||
if (c.sauce) {
|
||||
parts.push(escHtml(c.sauce.libelle));
|
||||
|
|
|
|||
|
|
@ -26,6 +26,20 @@ import { refreshCartBadge } from './nav.js';
|
|||
/* slot_type de l'API -> champ de composition attendu par le rendu panier existant. */
|
||||
const SLOT_FIELD = { side: 'accompagnement', drink: 'boisson', sauce: 'sauce' };
|
||||
|
||||
/**
|
||||
* Libelle a afficher pour une option selon le format. En Maxi ('M'), un
|
||||
* accompagnement a une variante agrandie (maxiNom, ex. "Grande Frite") : c'est ce
|
||||
* nom que le client doit voir au moment de CHOISIR, pas le "Moyenne Frite" de base.
|
||||
* Sans maxiNom (ex. les boissons, que le menu Maxi n'agrandit pas) ou en Normal,
|
||||
* on garde le nom de base. Pur.
|
||||
* @param {Object} option — produit borne {nom, maxiNom?}
|
||||
* @param {'N'|'M'} size
|
||||
* @returns {string}
|
||||
*/
|
||||
export function optionLabel(option, size) {
|
||||
return (size === 'M' && option.maxiNom) ? option.maxiNom : option.nom;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Fonctions PURES (cible des tests, sans DOM ni fetch) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -89,9 +103,13 @@ export function buildMenuCartItem(menu, model, { size, selections }) {
|
|||
if (!chosen) continue; // slot optionnel laisse "sans"
|
||||
const field = SLOT_FIELD[slot.slotType];
|
||||
if (!field) continue;
|
||||
// libelle PORTE le nom affiche : en Maxi, l'accompagnement prend sa variante
|
||||
// ("Grande Frite") ; la boisson n'a pas de maxiNom (le menu Maxi ne l'agrandit
|
||||
// pas) donc garde son nom de base. Plus de suffixe " grande" cote rendu.
|
||||
const libelle = (isMaxi && chosen.maxiNom) ? chosen.maxiNom : chosen.nom;
|
||||
composition[field] = field === 'sauce'
|
||||
? { id: chosen.id, libelle: chosen.nom }
|
||||
: { id: chosen.id, libelle: chosen.nom, taille };
|
||||
: { id: chosen.id, libelle, taille };
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -294,18 +312,23 @@ function renderSlotStep(body, footer, modal, state, slot) {
|
|||
<span class="composer-card__name">Sans</span>
|
||||
</button>
|
||||
</li>` : ''}
|
||||
${slot.options.map(o => `
|
||||
${slot.options.map(o => {
|
||||
// En Maxi, l'accompagnement s'affiche sous sa variante agrandie
|
||||
// ("Grande Frite") : le client choisit en connaissance de cause.
|
||||
const label = optionLabel(o, state.size);
|
||||
return `
|
||||
<li>
|
||||
<button class="composer-card ${state.selections[slot.id] === o.id ? 'composer-card--selected' : ''}"
|
||||
type="button" data-pid="${o.id}"
|
||||
aria-pressed="${state.selections[slot.id] === o.id}"
|
||||
aria-label="${escHtml(o.nom)}">
|
||||
<img class="composer-card__image" src="${escHtml(o.image)}" alt="${escHtml(o.nom)}"
|
||||
aria-label="${escHtml(label)}">
|
||||
<img class="composer-card__image" src="${escHtml(o.image)}" alt="${escHtml(label)}"
|
||||
onerror="this.src='assets/images/ui/logo.png';">
|
||||
<span class="composer-card__name">${escHtml(o.nom)}</span>
|
||||
<span class="composer-card__name">${escHtml(label)}</span>
|
||||
</button>
|
||||
</li>
|
||||
`).join('')}
|
||||
`;
|
||||
}).join('')}
|
||||
</ul>
|
||||
`;
|
||||
body.querySelectorAll('#slot-grid .composer-card').forEach(btn => {
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ final class CatalogueControllerTest extends TestCase
|
|||
'id' => '12', 'category_id' => '3', 'name' => 'Cheeseburger',
|
||||
'description' => 'Pain, steak, cheddar', 'price_cents' => '890',
|
||||
'vat_rate' => '100', 'image_path' => 'cheese.png', 'display_order' => '1',
|
||||
// LEFT JOIN variante Maxi : NULL pour un produit sans variante.
|
||||
'maxi_variant_name' => null,
|
||||
],
|
||||
];
|
||||
|
||||
|
|
@ -113,7 +115,7 @@ final class CatalogueControllerTest extends TestCase
|
|||
|
||||
$product = $payload['data'][0];
|
||||
self::assertSame(
|
||||
['id', 'category_id', 'name', 'description', 'price_cents', 'image_path', 'display_order', 'sizes'],
|
||||
['id', 'category_id', 'name', 'description', 'price_cents', 'image_path', 'display_order', 'maxi_variant_name', 'sizes'],
|
||||
array_keys($product),
|
||||
);
|
||||
self::assertSame(12, $product['id']);
|
||||
|
|
@ -121,9 +123,31 @@ final class CatalogueControllerTest extends TestCase
|
|||
self::assertSame(890, $product['price_cents']); // chaine -> int
|
||||
self::assertArrayNotHasKey('vat_rate', $product); // fiscal interne, non expose
|
||||
self::assertArrayNotHasKey('is_available', $product); // toujours dispo ici -> non expose
|
||||
self::assertNull($product['maxi_variant_name']); // pas de variante -> null
|
||||
self::assertSame([], $product['sizes']); // produit mono-taille -> sizes vide
|
||||
}
|
||||
|
||||
public function testProductsListExposesMaxiVariantName(): void
|
||||
{
|
||||
$db = new FakeCatalogueDatabase();
|
||||
// "Moyenne Frite" (accompagnement) a une variante Maxi "Grande Frite" : le
|
||||
// LEFT JOIN remonte mv.name AS maxi_variant_name, expose tel quel a la borne.
|
||||
$db->productsRows = [
|
||||
[
|
||||
'id' => '23', 'category_id' => '4', 'name' => 'Moyenne Frite',
|
||||
'description' => null, 'price_cents' => '250',
|
||||
'image_path' => 'frite.png', 'display_order' => '1',
|
||||
'maxi_variant_name' => 'Grande Frite',
|
||||
],
|
||||
];
|
||||
|
||||
$response = $this->controller($db, '/api/products')->products();
|
||||
|
||||
self::assertSame(200, $response->status());
|
||||
$product = $this->decode($response->body())['data'][0];
|
||||
self::assertSame('Grande Frite', $product['maxi_variant_name']);
|
||||
}
|
||||
|
||||
public function testProductsListPresentsSizesArrayForDrinkWithVariants(): void
|
||||
{
|
||||
$db = new FakeCatalogueDatabase();
|
||||
|
|
@ -205,6 +229,8 @@ final class CatalogueControllerTest extends TestCase
|
|||
'id' => '12', 'category_id' => '3', 'name' => 'Cheeseburger',
|
||||
'description' => null, 'price_cents' => '890', 'vat_rate' => '100',
|
||||
'image_path' => null, 'display_order' => '1',
|
||||
// Detail d'un accompagnement avec variante Maxi : le nom doit ressortir.
|
||||
'maxi_variant_name' => 'Grande Frite',
|
||||
];
|
||||
|
||||
$response = $this->controller($db, '/api/products/12')->product(['id' => '12']);
|
||||
|
|
@ -215,6 +241,7 @@ final class CatalogueControllerTest extends TestCase
|
|||
self::assertSame(12, $product['id']);
|
||||
self::assertSame(890, $product['price_cents']);
|
||||
self::assertNull($product['description']);
|
||||
self::assertSame('Grande Frite', $product['maxi_variant_name']); // variante exposee
|
||||
self::assertArrayNotHasKey('vat_rate', $product);
|
||||
// L'id a bien ete lie a la lecture, converti en entier (le repo a recu :id = 12).
|
||||
self::assertSame(12, $db->reads[0]['params']['id'] ?? null);
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import { test, before } from 'node:test';
|
|||
import assert from 'node:assert/strict';
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
||||
let buildComposerSteps, buildMenuCartItem, selectionsComplete, composerIsViable;
|
||||
let buildComposerSteps, buildMenuCartItem, selectionsComplete, composerIsViable, optionLabel;
|
||||
|
||||
before(async () => {
|
||||
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', { url: 'https://kiosk.test/product.html' });
|
||||
global.window = dom.window;
|
||||
global.document = dom.window.document;
|
||||
global.localStorage = dom.window.localStorage;
|
||||
({ buildComposerSteps, buildMenuCartItem, selectionsComplete, composerIsViable } =
|
||||
({ buildComposerSteps, buildMenuCartItem, selectionsComplete, composerIsViable, optionLabel } =
|
||||
await import('../../src/public/borne/assets/js/page-product-menu.js'));
|
||||
});
|
||||
|
||||
|
|
@ -33,12 +33,14 @@ const detail = () => ({
|
|||
});
|
||||
|
||||
const byId = () => ({
|
||||
100: { id: 100, nom: 'Le 280', prix: 0, image: 'b.png', type: 'produit' },
|
||||
22: { id: 22, nom: 'Frites', prix: 0, image: 'f.png', type: 'produit' },
|
||||
23: { id: 23, nom: 'Potatoes', prix: 0, image: 'p.png', type: 'produit' },
|
||||
14: { id: 14, nom: 'Coca', prix: 0, image: 'c.png', type: 'produit' },
|
||||
15: { id: 15, nom: 'Eau', prix: 0, image: 'e.png', type: 'produit' },
|
||||
47: { id: 47, nom: 'Ketchup', prix: 0, image: 'k.png', type: 'produit' },
|
||||
100: { id: 100, nom: 'Le 280', prix: 0, image: 'b.png', type: 'produit', maxiNom: null },
|
||||
// Accompagnements : variante Maxi (maxiNom) renseignee -> agrandissable.
|
||||
22: { id: 22, nom: 'Moyenne Frite', prix: 0, image: 'f.png', type: 'produit', maxiNom: 'Grande Frite' },
|
||||
23: { id: 23, nom: 'Potatoes', prix: 0, image: 'p.png', type: 'produit', maxiNom: 'Grande Potatoes' },
|
||||
// Boissons : pas de variante Maxi (le menu Maxi n'agrandit pas la boisson).
|
||||
14: { id: 14, nom: 'Coca', prix: 0, image: 'c.png', type: 'produit', maxiNom: null },
|
||||
15: { id: 15, nom: 'Eau', prix: 0, image: 'e.png', type: 'produit', maxiNom: null },
|
||||
47: { id: 47, nom: 'Ketchup', prix: 0, image: 'k.png', type: 'produit', maxiNom: null },
|
||||
});
|
||||
|
||||
const menu = { id: 1, nom: 'Menu Le 280', image: 'b.png', type: 'menu' };
|
||||
|
|
@ -70,7 +72,8 @@ test('buildMenuCartItem Normal: prix normal, pas de supplement, taille N, compos
|
|||
assert.equal(item.prix_cents, 880);
|
||||
assert.equal(item.supplement_cents, 0);
|
||||
assert.equal(item.composition.burger.libelle, 'Le 280');
|
||||
assert.deepEqual(item.composition.accompagnement, { id: 22, libelle: 'Frites', taille: 'N' });
|
||||
// Normal : l'accompagnement garde son nom de base (pas la variante Maxi).
|
||||
assert.deepEqual(item.composition.accompagnement, { id: 22, libelle: 'Moyenne Frite', taille: 'N' });
|
||||
assert.deepEqual(item.composition.boisson, { id: 14, libelle: 'Coca', taille: 'N' });
|
||||
assert.deepEqual(item.composition.sauce, { id: 47, libelle: 'Ketchup' });
|
||||
});
|
||||
|
|
@ -84,6 +87,31 @@ test('buildMenuCartItem Maxi: supplement = maxi - normal, taille G sur side/drin
|
|||
assert.equal(item.composition.boisson.taille, 'G');
|
||||
});
|
||||
|
||||
test('buildMenuCartItem Maxi: l accompagnement prend sa variante (Grande Frite), pas le nom de base', () => {
|
||||
const m = buildComposerSteps(detail(), byId());
|
||||
const item = buildMenuCartItem(menu, m, { size: 'M', selections: { 1: 14, 16: 22, 31: 47 } });
|
||||
assert.equal(item.composition.accompagnement.libelle, 'Grande Frite'); // pas "Moyenne Frite"
|
||||
// Boisson sans maxiNom : garde son nom de base meme en Maxi (le Maxi ne l agrandit pas).
|
||||
assert.equal(item.composition.boisson.libelle, 'Coca');
|
||||
});
|
||||
|
||||
test('buildMenuCartItem Normal: l accompagnement garde "Moyenne Frite" (pas de variante)', () => {
|
||||
const m = buildComposerSteps(detail(), byId());
|
||||
const item = buildMenuCartItem(menu, m, { size: 'N', selections: { 1: 14, 16: 22, 31: 47 } });
|
||||
assert.equal(item.composition.accompagnement.libelle, 'Moyenne Frite');
|
||||
});
|
||||
|
||||
/* --- optionLabel (pur) : libelle affiche au CHOIX selon le format -------- */
|
||||
|
||||
test('optionLabel: Maxi affiche la variante quand elle existe, sinon le nom de base', () => {
|
||||
const frite = { nom: 'Moyenne Frite', maxiNom: 'Grande Frite' };
|
||||
const coca = { nom: 'Coca', maxiNom: null };
|
||||
assert.equal(optionLabel(frite, 'M'), 'Grande Frite');
|
||||
assert.equal(optionLabel(frite, 'N'), 'Moyenne Frite');
|
||||
assert.equal(optionLabel(coca, 'M'), 'Coca'); // pas de variante -> nom de base
|
||||
assert.equal(optionLabel(coca, 'N'), 'Coca');
|
||||
});
|
||||
|
||||
test('buildMenuCartItem: slot optionnel non choisi -> champ absent de composition', () => {
|
||||
const m = buildComposerSteps(detail(), byId());
|
||||
const item = buildMenuCartItem(menu, m, { size: 'N', selections: { 1: 14, 16: 22 } }); // pas de sauce
|
||||
|
|
|
|||
|
|
@ -68,10 +68,20 @@ test('loadProducts groupe les produits par slug a la forme borne (type produit)'
|
|||
const data = await loadProducts();
|
||||
assert.deepEqual(data.burgers, [
|
||||
// sizes (R4) : tableau vide par defaut quand l'API n'en renvoie pas.
|
||||
{ id: 10, nom: 'Big Mac', prix: 600, image: 'assets/images/produits/burgers/bigmac.png', type: 'produit', sizes: [] },
|
||||
// maxiNom : null par defaut quand l'API n'envoie pas maxi_variant_name.
|
||||
{ id: 10, nom: 'Big Mac', prix: 600, image: 'assets/images/produits/burgers/bigmac.png', type: 'produit', maxiNom: null, sizes: [] },
|
||||
]);
|
||||
});
|
||||
|
||||
test('loadProducts reporte maxi_variant_name -> maxiNom (variante Maxi de l accompagnement)', async () => {
|
||||
const fx = fixtures();
|
||||
fx['/api/products'].data[0].maxi_variant_name = 'Grande Frite';
|
||||
const { loadProducts } = await freshData(fx);
|
||||
|
||||
const data = await loadProducts();
|
||||
assert.equal(data.burgers[0].maxiNom, 'Grande Frite');
|
||||
});
|
||||
|
||||
test('loadProducts reporte le tableau sizes du produit (R4) tel quel', async () => {
|
||||
const fx = fixtures();
|
||||
fx['/api/products'].data[0].sizes = [
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ const menu = (over = {}) => ({
|
|||
supplement_cents: 50, image: 'm.png',
|
||||
composition: {
|
||||
burger: { libelle: 'Big Mac', options: ['sans-oignon', 'avec-fromage'] },
|
||||
accompagnement: { libelle: 'Frites', taille: 'G' },
|
||||
boisson: { libelle: 'Coca', taille: 'M' },
|
||||
// Maxi : l accompagnement porte deja sa variante par NOM (le serveur substitue
|
||||
// Moyenne -> Grande). Le libelle fait foi, plus de suffixe " grande".
|
||||
accompagnement: { libelle: 'Grande Frite', taille: 'G' },
|
||||
boisson: { libelle: 'Coca', taille: 'G' },
|
||||
sauce: { libelle: 'Ketchup' },
|
||||
},
|
||||
...over,
|
||||
|
|
@ -63,14 +65,19 @@ test('compositionLabels: undefined -> []', () => {
|
|||
assert.deepEqual(compositionLabels(undefined), []);
|
||||
});
|
||||
|
||||
test('compositionLabels: liste burger(options)/accompagnement(taille)/boisson/sauce', () => {
|
||||
test('compositionLabels: libelle fait foi, le suffixe " grande" trompeur est supprime', () => {
|
||||
const labels = compositionLabels(menu().composition);
|
||||
assert.deepEqual(labels, [
|
||||
'Big Mac (sans oignon, avec fromage)',
|
||||
'Frites grande',
|
||||
'Coca',
|
||||
'Grande Frite', // variante par nom, plus de "Moyenne Frite grande"
|
||||
'Coca', // boisson non agrandie : pas de faux " grande"
|
||||
'Ketchup',
|
||||
]);
|
||||
// Garde-fou explicite contre la regression du bug rapporte.
|
||||
const sideLabel = labels[1];
|
||||
assert.equal(sideLabel.includes('Moyenne Frite grande'), false);
|
||||
assert.equal(sideLabel.endsWith(' grande'), false);
|
||||
assert.ok(sideLabel.includes('Grande Frite'));
|
||||
});
|
||||
|
||||
test('compositionLabels: composants absents ignores sans jeter', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue