def test_reference_has_no_inferred_name(self) -> None: sub_recipe = SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), ), False) assert compile(["spam\nspam\nspam"]) == [ Recipe(( sub_recipe, Reference(sub_recipe, 0), Reference(sub_recipe, 0), )), ]
def test_name_validation(self) -> None: sr = SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), SVS("bar"))) # Should work Reference(sr) Reference(sr, 0) Reference(sr, 1) # Unknown name with pytest.raises(OutputIndexError): Reference(sr, 2)
def test_scale(self) -> None: sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)), (SVS([2, " blocks of spam"]), )) sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)), (SVS([6, " blocks of spam"]), )) assert Reference(sr_2, 0).scale(3) == Reference(sr_6, 0) assert Reference(sr_2, 0, Quantity(100, "g")).scale(3) == Reference( sr_6, 0, Quantity(300, "g"), )
def test_scale(self) -> None: sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)), (SVS("spam"), )) ref_sr_2 = Reference(sr_2) sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)), (SVS("spam"), )) ref_sr_6 = Reference(sr_6) first_rec_2 = Recipe((sr_2, )) second_rec_2 = Recipe((ref_sr_2, ), follows=first_rec_2) first_rec_6 = Recipe((sr_6, )) second_rec_6 = Recipe((ref_sr_6, ), follows=first_rec_6) assert first_rec_2.scale(3) == first_rec_6 assert second_rec_2.scale(3) == second_rec_6
def test_separate_recipes(self) -> None: compiled = compile_markdown( dedent(""" Fried egg: ```recipe 1 egg ``` ```recipe fry(egg) ``` Boiled egg: ```new-recipe 2 egg ``` ```recipe boil(egg) ``` """).strip()) e1 = SubRecipe( Ingredient(SVS("egg"), Quantity(1)), (SVS("egg"), ), show_output_names=False, ) r1 = Recipe((e1, )) r2 = Recipe((Step(SVS("fry"), (Reference(e1), )), ), follows=r1) e2 = SubRecipe( Ingredient(SVS("egg"), Quantity(2)), (SVS("egg"), ), show_output_names=False, ) r3 = Recipe((e2, )) r4 = Recipe((Step(SVS("boil"), (Reference(e2), )), ), follows=r3) assert compiled.recipes == [[r1, r2], [r3, r4]] # Check anchor IDs are unique too html = compiled.render() assert 'id="recipe-egg"' in html assert 'href="#recipe-egg"' in html assert 'id="recipe2-egg"' in html assert 'href="#recipe2-egg"' in html
def test_nested_reference_to_sub_recipe_not_in_recipe(self) -> None: external_sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), )) ref = Reference(external_sr) step = Step(SVS("bar"), (ref, )) with pytest.raises(ReferenceToInvalidSubRecipeError): Recipe((step, ))
def _compile_reference( self, ast_reference: ast.Reference) -> Union[Ingredient, Reference]: name = compile_string(ast_reference.name) normalised_name = normalise_output_name(name) if normalised_name in self._named_outputs: # Name refers to a SubRecipe output, this is a Reference output = self._named_outputs[normalised_name] reference = Reference( sub_recipe=output.sub_recipe, output_index=output.output_index, amount=self._compile_quantity_or_proportion( ast_reference.quantity_or_proportion), ) output.references.append((reference, self._current_recipe_index)) return reference else: # This is an ingredient if isinstance(ast_reference.quantity_or_proportion, ast.Proportion): raise ProportionGivenForIngredientError.from_ast_reference( self._sources[self._current_recipe_index], ast_reference, ) return Ingredient( description=name, quantity=(self._compile_quantity( ast_reference.quantity_or_proportion) if ast_reference.quantity_or_proportion is not None else None), )
def test_recipes_split_across_blocks(self) -> None: compiled = compile_markdown( dedent(""" A recipe in two parts. Part one: sauce = boil down(tomatoes, water) Part two: pour over(pasta, sauce) """).strip()) sr = SubRecipe( Step( SVS("boil down"), ( Ingredient(SVS("tomatoes")), Ingredient(SVS("water")), ), ), (SVS("sauce"), ), ) r1 = Recipe((sr, )) r2 = Recipe( (Step( SVS("pour over"), ( Ingredient(SVS("pasta")), Reference(sr), ), ), ), follows=r1, ) assert compiled.recipes == [[r1, r2]]
def test_reference_to_nested_sub_recipe(self) -> None: nested_sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), )) step = Step(SVS("scramble"), (nested_sr, )) ref = Reference(nested_sr) with pytest.raises(ReferenceToInvalidSubRecipeError): Recipe((step, ref))
def test_valid_references(self) -> None: sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), )) ref1 = Reference(sr) # Shouldn't fail rec1 = Recipe((sr, ref1)) # Also shouldn't fail (since marked as follows) ref2 = Reference(sr) rec2 = Recipe((ref2, ), follows=rec1) # Chained references ref3 = Reference(sr) Recipe((ref3, ), follows=rec2) # Should fail: not referenced ref4 = Reference(sr) with pytest.raises(ReferenceToInvalidSubRecipeError): Recipe((ref4, ))
def test_reference_compilation(self) -> None: sub_recipe = SubRecipe( Step(SVS("open"), (Ingredient(SVS("spam")), )), (SVS("spam"), SVS("tin")), True, ) assert compile([ "spam, tin = open(spam)\nspam\n1/3*spam\n25% of the spam\nleft over spam\n2 'tin'\n50g spam" # noqa: E501 ]) == [ Recipe(( sub_recipe, # spam Reference(sub_recipe, 0, Proportion(1.0)), # 1/3*spam Reference(sub_recipe, 0, Proportion(Fraction(1, 3), preposition="*")), # 25% of the spam Reference( sub_recipe, 0, Proportion(0.25, percentage=True, preposition="% of the"), ), # remaining Reference(sub_recipe, 0, Proportion(None, remainder_wording="left over")), # 2 tin Reference(sub_recipe, 1, Quantity(2.0)), # 50g spam Reference(sub_recipe, 0, Quantity(50.0, "g")), )), ]
def test_substitute(self) -> None: a = Ingredient(SVS("a")) b = SubRecipe(a, (SVS("b"), )) c = Ingredient(SVS("c")) d = SubRecipe(c, (SVS("d"), )) orig = Reference(b, 0) assert orig.substitute(a, c) == Reference(SubRecipe(c, (SVS("b"), )), 0) assert orig.substitute(b, d) == Reference(SubRecipe(c, (SVS("d"), )), 0) assert orig.substitute(orig, c) == c
def test_dont_inline_partial_uses_of_a_subrecipe(self) -> None: sub_recipe = SubRecipe( Ingredient(SVS("spam"), Quantity(100, "g")), (SVS("spam"), ), False, ) assert compile(["100g spam\nfry(50g spam, eggs)"]) == [ Recipe(( sub_recipe, Step( SVS("fry"), ( Reference(sub_recipe, 0, Quantity(50, "g")), Ingredient(SVS("eggs")), ), ), )) ]
def test_dont_inline_multi_output_subrecipes(self) -> None: sub_recipe = SubRecipe( Ingredient(SVS("spam")), (SVS("meat"), SVS("tin")), True, ) assert compile(["meat, tin = spam\nfry(meat, eggs)"]) == [ Recipe(( sub_recipe, Step( SVS("fry"), ( Reference(sub_recipe, 0), Ingredient(SVS("eggs")), ), ), )) ]
def test_dont_inline_definitions_from_earlier_blocks(self) -> None: sub_recipe = SubRecipe( Ingredient(SVS("spam"), Quantity(100, "g")), (SVS("spam"), ), False, ) recipe0 = Recipe((sub_recipe, )) recipe1 = Recipe( (Step( SVS("fry"), ( Reference(sub_recipe, 0, Quantity(50, "g")), Ingredient(SVS("eggs")), ), ), ), follows=recipe0, ) assert compile(["100g spam", "fry(50g spam, eggs)"]) == [recipe0, recipe1]
SVS("fOo")) assert normalise_output_name(SVS(" Foo ")) != normalise_output_name( SVS("bAr")) @pytest.mark.parametrize( "recipe_tree, exp", [ # Workable cases (Ingredient(SVS("spam")), SVS("spam")), (Step(SVS("fry"), (Ingredient(SVS("spam")), )), SVS("spam")), # Can't name step with several inputs (Step(SVS("fry"), (Ingredient(SVS("spam")), Ingredient(SVS("eggs")))), None), # Can't name references (Reference(SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), )), 0), None), # Can't name sub recipes (they're already named!) (SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), )), None), ], ) def test_infer_output_name(recipe_tree: RecipeTreeNode, exp: Optional[SVS]) -> None: assert infer_output_name(recipe_tree) == exp @pytest.mark.parametrize( "recipe_tree, exp", [ # Workable cases (Ingredient(SVS("spam")), None), (Ingredient(SVS("spam"), Quantity(100)), Quantity(100)),
def test_generate_subrecipe_output_id() -> None: sub_recipe = SubRecipe( Ingredient(SVS("spam")), (SVS("foo"), SVS(["foo bar ", 123, " baz?"])), ) assert generate_subrecipe_output_id(sub_recipe, 0, "qux-") == "qux-foo" assert generate_subrecipe_output_id(sub_recipe, 1, "qux-") == "qux-foo-bar-123-baz" @pytest.mark.parametrize( "reference, exp", [ # No amount (consumes whole thing) ( Reference(SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), )), 0, Proportion(1)), '<a href="#sub-recipe-foo">foo</a>', ), # Non-whole proportion ( Reference(SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), )), 0, Proportion(None)), ('<a href="#sub-recipe-foo">' '<span class="rg-proportion-remainder">remaining</span> foo' "</a>"), ), ( Reference( SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), )), 0, Proportion(0.5, preposition=" *"),
def test_reference() -> None: sub_recipe = SubRecipe(Ingredient(SVS("spam")), (SVS("out"), )) reference = Reference(sub_recipe, 0) assert recipe_tree_to_table(reference) == set_border_around_table( Table.from_dict({(0, 0): Cell(reference)}), BorderType.sub_recipe)
def test_reference(self) -> None: assert (render_cell( Cell( Reference(SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), )), 0)) ) == '<td class="rg-reference"><a href="#sub-recipe-foo">foo</a></td>')
def test_reference_to_sub_recipe_later_in_recipe(self) -> None: later_sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), )) ref = Reference(later_sr) with pytest.raises(ReferenceToInvalidSubRecipeError): Recipe((ref, later_sr))