Beispiel #1
0
    def test_scale(self) -> None:
        sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)),
                         (SVS([2, "spams"]), ))
        sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)),
                         (SVS([6, "spams"]), ))

        assert sr_2.scale(3) == sr_6
 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")),
         )),
     ]
Beispiel #3
0
    def test_scale(self) -> None:
        step = Step(SVS(["fry in ", 4, " blocks"]),
                    (Ingredient(SVS("spam"), Quantity(2)), ))

        assert step.scale(3) == Step(
            SVS(["fry in ", 12, " blocks"]),
            (Ingredient(SVS("spam"), Quantity(6)), ),
        )
Beispiel #4
0
    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"),
        )
Beispiel #5
0
    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_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_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]
 def test_ingredient_compilation(self) -> None:
     assert compile(
         ["500g spam\n2 eggs\n1 kg foo\n1 can of dog food\nheat"]) == [
             Recipe((
                 # With unit
                 SubRecipe(
                     Ingredient(SVS("spam"), Quantity(500.0, "g")),
                     (SVS("spam"), ),
                     False,
                 ),
                 # No unit
                 SubRecipe(
                     Ingredient(SVS("eggs"), Quantity(2.0)),
                     (SVS("eggs"), ),
                     False,
                 ),
                 # With spacing between number and unit
                 SubRecipe(
                     Ingredient(SVS("foo"),
                                Quantity(1.0, "kg",
                                         value_unit_spacing=" ")),
                     (SVS("foo"), ),
                     False,
                 ),
                 # With spacing between number and unit
                 SubRecipe(
                     Ingredient(
                         SVS("dog food"),
                         Quantity(
                             1,
                             "can",
                             value_unit_spacing=" ",
                             preposition=" of",
                         ),
                     ),
                     (SVS("dog food"), ),
                     False,
                 ),
                 # No quantity
                 SubRecipe(Ingredient(SVS("heat")), (SVS("heat"), ), False),
             ))
         ]
Beispiel #10
0
 def test_inlining_single_references(self, quantity_spec: str) -> None:
     assert compile(
         [f"meat = 10g spam, sliced\nfry({quantity_spec} meat, eggs)"]) == [
             Recipe((Step(
                 SVS("fry"),
                 (
                     Step(
                         SVS("sliced"),
                         (Ingredient(SVS("spam"), Quantity(10, "g")), ),
                     ),
                     Ingredient(SVS("eggs")),
                 ),
             ), ))
         ]
Beispiel #11
0
    def test_recipe_code_blocks_and_scaled_rendering(self,
                                                     source: str) -> None:
        compiled = compile_markdown(dedent(source).strip())
        assert compiled.title == "A recipe"
        assert compiled.servings == 2
        assert compiled.recipes == [[
            Recipe((Step(
                SVS("fry"),
                (
                    Ingredient(SVS("spam"), Quantity(100, "g")),
                    Ingredient(SVS("eggs"), Quantity(2)),
                ),
            ), )),
        ]]
        assert (compiled.render(2) == dedent("""
                <header><h1 class="rg-title-scalable">A recipe <span class="rg-serving-count">for <span class="rg-scaled-value">4</span></span></h1><p>Rescaled from <span class="rg-original-servings">2 servings</span>.</p></header>
                <div class="rg-recipe-block">
                  <table class="rg-table">
                    <tr>
                      <td class="rg-ingredient rg-border-left-sub-recipe rg-border-top-sub-recipe">
                        <span class="rg-quantity-with-conversions rg-scaled-value" tabindex="0">
                          200g<ul class="rg-quantity-conversions">
                            <li><sup>1</sup>&frasl;<sub>5</sub>kg</li>
                            <li>0.441lb</li>
                            <li>7.05oz</li>
                          </ul>
                        </span> spam
                      </td>
                      <td class="rg-step rg-border-right-sub-recipe rg-border-top-sub-recipe rg-border-bottom-sub-recipe" rowspan="2">fry</td>
                    </tr>
                    <tr><td class="rg-ingredient rg-border-left-sub-recipe rg-border-bottom-sub-recipe"><span class="rg-quantity-unitless rg-scaled-value">4</span> eggs</td></tr>
                  </table>
                </div><p>Ta-da!</p>
            """

                                             # noqa: E501
                                             ).lstrip())
Beispiel #12
0
    def _compile_quantity(self, ast_quantity: ast.Quantity) -> Quantity:
        unit: Optional[str] = None
        if ast_quantity.unit is not None:
            unit_string = compile_string(ast_quantity.unit)

            # The grammar disallows scaled values in unit names, this assertion
            # is just a sanity check to that effect
            assert unit_string == unit_string.scale(0)

            unit = str(unit_string)

        return Quantity(
            value=ast_quantity.value,
            unit=unit,
            value_unit_spacing=ast_quantity.value_unit_spacing,
            preposition=ast_quantity.preposition,
        )
Beispiel #13
0
 def test_inlines_within_inlines(self) -> None:
     recipe = Recipe((Step(
         SVS("boil"),
         (
             SubRecipe(
                 Step(
                     SVS("fry"),
                     (Ingredient(SVS("spam"), Quantity(100, "g")), ),
                 ),
                 (SVS("fried spam"), ),
             ),
             Ingredient(SVS("water")),
         ),
     ), ))
     assert compile([
         "100g spam\nfried spam := fry(spam)\nboil(fried spam, water)"
     ]) == [recipe]
Beispiel #14
0
 def test_inlining_within_a_block(self) -> None:
     recipe0 = Recipe((SubRecipe(Ingredient(SVS("egg")), (SVS("egg"), ),
                                 show_output_names=False), ))
     recipe1 = Recipe(
         (Step(SVS("fry"),
               (Ingredient(SVS("spam"), Quantity(50, "g")), )), ),
         follows=recipe0,
     )
     recipe2 = Recipe(
         (SubRecipe(Ingredient(SVS("potato")), (SVS("potato"), ),
                    show_output_names=False), ),
         follows=recipe1,
     )
     assert compile(["egg", "50g spam\nfry(spam)", "potato"]) == [
         recipe0,
         recipe1,
         recipe2,
     ]
Beispiel #15
0
class TestQuantity:
    @pytest.mark.parametrize(
        "a, b, exp",
        [
            # Unitless
            (Quantity(1), Quantity(1), True),
            (Quantity(1), Quantity(2), False),
            (Quantity(1), Quantity(1, "g"), False),
            (Quantity(1, "g"), Quantity(1), False),
            # Same unit
            (Quantity(1, "g"), Quantity(1, "g"), True),
            (Quantity(1, "g"), Quantity(1, "G"), True),
            (Quantity(1, "g"), Quantity(2, "g"), False),
            (Quantity(1, "g"), Quantity(1, "kg"), False),
            # Unit conversion used
            (Quantity(1, "pounds"), Quantity(16, "ounces"), True),
            (Quantity(1, "pounds"), Quantity(17, "ounces"), False),
            # Incompatible, but valid units
            (Quantity(1, "kg"), Quantity(1, "l"), False),
            # Compatible but unknown units
            (Quantity(123, "foo"), Quantity(123, "foo"), True),
            (Quantity(123, "FOO"), Quantity(123, "foo"), True),
            # Floating point errors mean an approximate comparison is required
            # here
            (Quantity(10, "g"), Quantity(0.01, "kg"), True),
        ],
    )
    def test_has_equal_value_to(self, a: Quantity, b: Quantity,
                                exp: bool) -> None:
        assert a.has_equal_value_to(b) is exp

    def test_scale(self) -> None:
        assert Quantity(123, "foo").scale(10) == Quantity(1230, "foo")
Beispiel #16
0
 def test_scale(self) -> None:
     assert Quantity(123, "foo").scale(10) == Quantity(1230, "foo")
Beispiel #17
0
                             (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)),
        (Ingredient(SVS("spam"), Quantity(100, "g")), Quantity(100, "g")),
        (Step(SVS("fry"),
              (Ingredient(SVS("spam"), Quantity(100)), )), Quantity(100)),
        (
            SubRecipe(Ingredient(SVS("spam"), Quantity(100)), (SVS("out"), )),
            Quantity(100),
        ),
        # Can't name step with several inputs
        (
            Step(
                SVS("fry"),
                (
                    Ingredient(SVS("spam"), Quantity(100)),
                    Ingredient(SVS("eggs"), Quantity(2)),
                ),
Beispiel #18
0
        # Fraction
        (Fraction(2, 3), "<sup>2</sup>&frasl;<sub>3</sub>"),
        # Improper fraction
        (Fraction(5, 3), "1 <sup>2</sup>&frasl;<sub>3</sub>"),
    ],
)
def test_render_number(number: Union[float, int, Fraction], exp: str) -> None:
    assert render_number(number) == exp


@pytest.mark.parametrize(
    "quantity, exp",
    [
        # Unitless
        (
            Quantity(123),
            '<span class="rg-quantity-unitless rg-scaled-value">123</span>',
        ),
        # Unitless with preposition
        (
            Quantity(123, preposition=" of>"),
            '<span class="rg-quantity-unitless rg-scaled-value">123</span> of&gt;',
        ),
        # Custom (unknown) unit with HTML char in
        (
            Quantity(123, "<foo>"),
            '<span class="rg-quantity-without-conversions rg-scaled-value">123&lt;foo&gt;</span>',
        ),
        # Custom unit with spacing and preposition
        (
            Quantity(
Beispiel #19
0
 def test_has_equal_value_to(self, a: Quantity, b: Quantity,
                             exp: bool) -> None:
     assert a.has_equal_value_to(b) is exp