]> Git — Sourcephile - julm/worksheets.git/blob - tests/RecipesSpec.hs
wip
[julm/worksheets.git] / tests / RecipesSpec.hs
1 {-# LANGUAGE OverloadedLists #-}
2 {-# LANGUAGE OverloadedStrings #-}
3
4 module RecipesSpec where
5
6 import Data.ByteString.Builder qualified as ByteString.Builder
7 import Data.Char qualified as Char
8 import Data.GenValidity.Map ()
9 import Data.GenValidity.Sequence ()
10 import Data.GenValidity.Set ()
11 import Data.GenValidity.Text ()
12 import Data.List qualified as List
13 import Data.Map.Strict qualified as Map
14 import Data.Ratio (denominator, numerator)
15 import Data.Set qualified as Set
16 import Data.Text qualified as Text
17 import Data.Text.Encoding qualified as Text
18 import Data.Text.Lazy.Encoding qualified as Text.Lazy
19 import Data.Text.Short qualified as ShortText
20 import Data.Validity.Map ()
21 import Data.Validity.Set ()
22 import Data.Validity.Text ()
23 import Graph.DOT qualified as DOT
24 import Language
25 import Language.French qualified as French
26 import Language.Pronunciation (ExampleLiteral (..), Lexeme (..), Rule (..), RuleLexemes, after, before, begining, ending, meaning, occurence, rule, silent, word)
27 import Language.Pronunciation qualified as Pron
28 import Paths_worksheets qualified as Self
29 import Recipes qualified as Recipes
30 import System.Directory qualified as IO
31 import System.FilePath (joinPath, pathSeparator, (<.>), (</>))
32 import System.FilePath.Posix qualified as File
33 import System.IO qualified as IO
34 import Test.Syd
35 import Text.Blaze.Html5.Attributes qualified as HA
36 import Utils.Pronunciation qualified as Pron
37 import Utils.Tests
38 import Wiktionary qualified
39 import Worksheets.Utils.HTML (Html, className, classes, cm, styles, (!))
40 import Worksheets.Utils.HTML qualified as HTML
41 import Worksheets.Utils.IPA qualified as IPA
42 import Worksheets.Utils.Paper qualified as Paper
43 import Worksheets.Utils.Prelude
44 import Worksheets.Utils.SQL qualified as SQL
45 import Prelude (error, even, mod)
46
47 spec :: HasCallStack => Spec
48 spec = do
49 describe "Recipes" do
50 recipeSpec recipeTarteAuCitron
51
52 recipeSpec recipe@Recipes.Recipe{..} = do
53 let title = recipeName & ShortText.unpack
54 outPath <- goldenPath title "dot"
55 it title do
56 goldenByteStringBuilderFile outPath do
57 return $ Recipes.recipeDOT recipe
58
59 -- let (blancDœuf, jauneDœuf) = séparerŒuf `app` (œuf & qty 2) `app` (bol & qty 2)
60 -- let beurreCubes = couperEnCubes `app` (beurre & grams 125) `app` (couteau & qty 1)
61 -- let pâteSablée = mélanger
62 recipeTarteAuCitron =
63 Recipes.Recipe
64 { recipeName = "TarteAuCitron"
65 , recipeFunctions =
66 [ "Préchauffer le four" :=
67 Recipes.Function
68 { functionInputs =
69 [ "four" := 1 & num & Recipes.TermDescr
70 ]
71 , functionSteps =
72 [ "Préchauffer le four 10min à 180°C"
73 ]
74 , functionOutputs =
75 [ "four préchauffé" := 1 & num
76 ]
77 }
78 , "Séparer les œufs" :=
79 Recipes.Function
80 { functionInputs =
81 [ "œuf" := 2 & num & Recipes.TermDescr
82 , "bol" := 2 & num & Recipes.TermDescr
83 ]
84 , functionSteps =
85 [ "Séparer les jaunes des blancs"
86 ]
87 , functionOutputs =
88 [ "blanc d'œuf" := 2 & num
89 , "jaune d'œuf" := 2 & num
90 ]
91 }
92 , "Couper le beurre" :=
93 Recipes.Function
94 { functionInputs =
95 [ "beurre" := 125 & grams & Recipes.TermDescr
96 , "couteau" := 1 & num & Recipes.TermDescr
97 ]
98 , functionSteps =
99 [ "Couper en petits cubes d'1cm de côté"
100 ]
101 , functionOutputs =
102 [ "beurre découpé" := 125 & grams
103 ]
104 }
105 , "Faire la pâte sableuse" :=
106 Recipes.Function
107 { functionInputs =
108 [ "beurre en cubes" :=
109 Recipes.TermRef
110 { termRefName = "Couper le beurre"
111 , termRefPort = Just "beurre découpé"
112 , termRefQuantity = Nothing
113 }
114 , "saladier" := 1 & num & Recipes.TermDescr
115 , "doigts" := 10 & num & Recipes.TermDescr
116 , "farine" := 200 & grams & Recipes.TermDescr
117 ]
118 , functionSteps =
119 [ "Mélanger (vite) les ingrédients sableux"
120 ]
121 , functionOutputs =
122 [ "pâte sableuse" := ""
123 ]
124 }
125 , "Faire la pâte mousseuse" :=
126 Recipes.Function
127 { functionInputs =
128 [ "jaune d'œuf" :=
129 Recipes.TermRef
130 { termRefName = "Séparer les œufs"
131 , termRefPort = Just "jaune d'œuf"
132 , termRefQuantity = Just $ 2 & num
133 }
134 , "sucre semoule" := 70 & grams & Recipes.TermDescr
135 , "saladier" := 1 & num & Recipes.TermDescr
136 , "fourchette" := 1 & num & Recipes.TermDescr
137 , "eau" := 2 & tablespoon & Recipes.TermDescr
138 ]
139 , functionSteps =
140 [ "Mélanger les ingrédients mousseux"
141 ]
142 , functionOutputs =
143 [ "pâte mousseuse" := ""
144 ]
145 }
146 , "Faire la pâte sablée" :=
147 Recipes.Function
148 { functionInputs =
149 [ "pâte mousseuse" :=
150 Recipes.TermRef
151 { termRefName = "Faire la pâte mousseuse"
152 , termRefPort = Just "pâte mousseuse"
153 , termRefQuantity = Nothing -- Just $ 1 & num
154 }
155 , "pâte sableuse" :=
156 Recipes.TermRef
157 { termRefName = "Faire la pâte sableuse"
158 , termRefPort = Just "pâte sableuse"
159 , termRefQuantity = Nothing -- Just $ 1 & num
160 }
161 ]
162 , functionSteps =
163 [ "Incorporer rapidement au couteau les éléments\nsans leur donner de corps"
164 , "Former une boule de pâte sablée avec les paumes"
165 , "Écraser 1 ou 2 fois la boule pour la rendre plus homogène"
166 ]
167 , functionOutputs =
168 [ "boule de pâte sablée" := 1 & num
169 ]
170 }
171 , "Cuire le fond de tarte" :=
172 Recipes.Function
173 { functionInputs =
174 [ "four préchauffé" :=
175 Recipes.TermRef
176 { termRefName = "Préchauffer le four"
177 , termRefPort = Just "four préchauffé"
178 , termRefQuantity = Nothing -- Just $ 1 & num
179 }
180 , "boule de pâte sablée" :=
181 Recipes.TermRef
182 { termRefName = "Faire la pâte sablée"
183 , termRefPort = Just "boule de pâte sablée"
184 , termRefQuantity = Nothing -- Just $ 1 & num
185 }
186 ]
187 , functionSteps =
188 [ "Foncer la pâte dans le moule"
189 , "(optionnel) Recouvrir la pâte de papier sulfurisé et de haricots sec"
190 , "Cuire la pâte à blanc 20 à 25 minutes"
191 ]
192 , functionOutputs =
193 [ "fond de tarte cuit" := 1 & num
194 ]
195 }
196 , "Zester les citrons" :=
197 Recipes.Function
198 { functionInputs =
199 [ "citron" := 2 & num & Recipes.TermDescr
200 ]
201 , functionSteps =
202 [ "Laver les citrons"
203 , "Râper les citrons"
204 ]
205 , functionOutputs =
206 [ "zeste de citron" := 1 & num
207 , "citron lavé" := 2 & num
208 ]
209 }
210 , "Presse les citrons" :=
211 Recipes.Function
212 { functionInputs =
213 [ "citron zesté" :=
214 Recipes.TermRef
215 { termRefName = "Zester les citrons"
216 , termRefPort = Just "citron lavé"
217 , termRefQuantity = Just $ 2 & num
218 }
219 , "citron" := 2 & num & Recipes.TermDescr
220 ]
221 , functionSteps =
222 [ "Presser les citrons"
223 ]
224 , functionOutputs =
225 [ "jus de citron" := 1 & num
226 ]
227 }
228 , "Faire la crème citron" :=
229 Recipes.Function
230 { functionInputs =
231 [ "zeste de citron" :=
232 Recipes.TermRef
233 { termRefName = "Zester les citrons"
234 , termRefPort = Just "zeste de citron"
235 , termRefQuantity = Nothing -- Just $ 2 & num
236 }
237 , "jus de citron" :=
238 Recipes.TermRef
239 { termRefName = "Presse les citrons"
240 , termRefPort = Just "jus de citron"
241 , termRefQuantity = Nothing -- Just $ 4 & num
242 }
243 , "sucre semoule" := 150 & grams & Recipes.TermDescr
244 , "farine de maïs" := 1 & tablespoon & Recipes.TermDescr
245 , "fouet" := 1 & num & Recipes.TermDescr
246 , "casserole" := 1 & num & Recipes.TermDescr
247 , "gasinière" := 1 & num & Recipes.TermDescr
248 ]
249 , functionSteps =
250 [ "Faire chauffer les ingrédients dans une casserole\nen remuant avec un fouet\nen faisant des cercles et des huits constamment\njusqu'à épaississement de la crème"
251 ]
252 , functionOutputs =
253 [ "crème citron" := 1 & num
254 ]
255 }
256 , "Faire la tarte au citron" :=
257 Recipes.Function
258 { functionInputs =
259 [ "fond de tarte cuit" :=
260 Recipes.TermRef
261 { termRefName = "Cuire le fond de tarte"
262 , termRefPort = Just "fond de tarte cuit"
263 , termRefQuantity = Just $ 1 & num
264 }
265 , "crème citron" :=
266 Recipes.TermRef
267 { termRefName = "Faire la crème citron"
268 , termRefPort = Just "crème citron"
269 , termRefQuantity = Just $ 1 & num
270 }
271 ]
272 , functionSteps =
273 [ "Verser sur le fond de tarte cuit"
274 , "Laisser refroidir"
275 ]
276 , functionOutputs =
277 [ "tarte au citron" := 1 & num
278 ]
279 }
280 , "Faire la meringue" :=
281 Recipes.Function
282 { functionInputs =
283 [ "blanc d'œuf" :=
284 Recipes.TermRef
285 { termRefName = "Séparer les œufs"
286 , termRefPort = Just "blanc d'œuf"
287 , termRefQuantity = Just $ 2 & num
288 }
289 , "sucre glace" := 100 & grams & Recipes.TermDescr
290 , "levure chimique" := 0.5 & teaspoon & Recipes.TermDescr
291 , "sel" := 1 & pincée & Recipes.TermDescr
292 ]
293 , functionSteps =
294 [ "Monter les blancs en neige"
295 , "Quand les blancs commencent à être fermes,\najouter le sucre puis la levure"
296 ]
297 , functionOutputs =
298 [ "meringue" := 1 & num
299 ]
300 }
301 , "Faire la tarte au citron meringuée" :=
302 Recipes.Function
303 { functionInputs =
304 [ "tarte au citron" :=
305 Recipes.TermRef
306 { termRefName = "Faire la tarte au citron"
307 , termRefPort = Just "tarte au citron"
308 , termRefQuantity = Just $ 1 & num
309 }
310 , "meringue" :=
311 Recipes.TermRef
312 { termRefName = "Faire la meringue"
313 , termRefPort = Just "meringue"
314 , termRefQuantity = Just $ 1 & num
315 }
316 , "four" := 1 & num & Recipes.TermDescr
317 ]
318 , functionSteps =
319 [ "Recouvrir la tarte au citron de meringue"
320 , "Enfourner la tarte jusqu'à ce que la meringue dore"
321 ]
322 , functionOutputs =
323 [ "tarte au citron meringuée" := 1 & num
324 ]
325 }
326 ]
327 }
328
329 grams :: Integer -> Recipes.Quantity
330 grams s = (ShortText.pack (show s) <> "g")
331 centiliters :: Integer -> Recipes.Quantity
332 centiliters s = (ShortText.pack (show s) <> "cl")
333 pincée :: Integer -> Recipes.Quantity
334 pincée s = (ShortText.pack (show s) <> "pincée")
335 tablespoon :: Rational -> Recipes.Quantity
336 tablespoon r
337 | denominator r == 1 = ShortText.pack $ show (numerator r) <> "càs"
338 | otherwise = ShortText.pack $ show (numerator r) <> "/" <> show (denominator r) <> "càs"
339 teaspoon :: Rational -> Recipes.Quantity
340 teaspoon r
341 | denominator r == 1 = ShortText.pack $ show (numerator r) <> "càc"
342 | otherwise = ShortText.pack $ show (numerator r) <> "/" <> show (denominator r) <> "càc"
343 num :: Integer -> Recipes.Quantity
344 num s = (ShortText.pack (show s) <> "×")