progressive

dev-lp
Matthew Butterick 9 years ago
parent 342673d6ce
commit 1852471612

@ -0,0 +1,9 @@
#lang racket
(module test racket
(require rackunit txexpr "pollen-lp.scrbl")
(#reader scribble/reader
;; always include this at the start of the test submodule
(check-txexprs-equal? (link "http://foo.com" "link text")
'(a ((href "http://foo.com")) "link text"))
(check-txexprs-equal? @link["http://foo.com"]{link text}
'(a ((href "http://foo.com")) "link textz"))))

@ -1,5 +1,170 @@
#lang racket/base
(require "pollen-lp.scrbl")
#;(require (submod "pollen-lp.rkt" test))
#lang pollen/mode racket/base
(link "foo")
#|
UNIT TESTS
Testing, as always, is optional, but strongly recommended. Unit tests are little one-line tests that
prove your function does what it says. As you refactor and reorganize your code, your unit tests will
let you know if you broke anything.
You can make unit tests with the `rackunit` library. Though you can put your unit tests in a separate
source file, I generally prefer to put them close to the function that they're testing. (For details
on the testing functions used below, see the docs for `rackunit`)
The ideal way to do this is with a `test` submodule. The code in a `test` submodule will only be used
a) when you run the file in DrRacket or
b) when `raco test` runs the file.
Otherwise, it is ignored.
We'll use the `module+` syntax for this. As the name suggests, `module+` creates a submodule that
incorporates everything else already in the source file. Moreover, all of our `module+ test` blocks
will be combined into a single submodule.
|#
(module+ test
(require rackunit txexpr "pollen-lp.scrbl") ;; always include this at the start of the test submodule
;; We use `check-txexprs-equal?` rather than `check-equal?` because it's a little more lenient:
;; it allows the attributes of two txexprs to be in a different order,
;; yet still be considered equal (because ordering of attributes is not semantically significant).
(check-txexprs-equal? (link "http://foo.com" "link text")
'(a ((href "http://foo.com")) "link text"))
;; The last test was fine, but it can be even better if we use a Pollen-mode command on the left.
;; That way, we can directly compare the command as it appears in Pollen input
;; with how it appears in the output.
(check-txexprs-equal? ◊link["http://foo.com"]{link text}
'(a ((href "http://foo.com")) "link text"))
;; It's wise to test as many valid input situations as you can.
(check-txexprs-equal? ◊link["http://foo.com" #:class 'main]{link text}
'(a ((href "http://foo.com")(class "main")) "link text"))
(check-txexprs-equal? ◊link["http://foo.com"]
'(a ((href "http://foo.com")) "http://foo.com"))
;; Strictly speaking, you could also write the last Pollen command like so:
(check-txexprs-equal? ◊link{http://foo.com} '(a ((href "http://foo.com")) "http://foo.com"))
;; That's not wrong. But in the interests of code readability,
;; I like to reserve the curly brackets in a Pollen command
;; for material that I expect to see displayed in the output
;; (e.g., textual and other content),
;; and use the square brackets for the other arguments.
;; You can also check that errors arise when they should.
;; Note that when testing for exceptions, you need to wrap your test expression in a function
;; (so that its evaluation can be delayed, otherwise you'd get the error immediately.)
;; The `(λ _ expression)` notation is a simple way.
;; (The `_` is the idiomatic way to notate something that will be ignored, in this case arguments.)
(check-exn exn:fail? (λ _ ◊link[])) ; no arguments
(check-exn exn:fail? (λ _ ◊link[#:invalid-keyword 42])) ; invalid keyword argument
(check-exn exn:fail? (λ _ ◊link[#f]))) ; invalid argument
;; For the sake of brevity, I'm going to write just one test for the remaining functions.
;; But you're encouraged to add more tests (or break the existing ones and see what happens).
(module+ test
;; notice that we use `buy-url` in our test result.
;; That way, if we change the value of `buy-url`, the test won't break.
(check-txexprs-equal? ◊buy-book-link{link text} `(a ((href ,buy-url)) "link text")))
(module+ test
(check-txexprs-equal? ◊buylink["http://foo.com"]{link text}
'(a ((href "http://foo.com")(class "buylink")) "link text")))
(module+ test
(check-txexprs-equal? ◊home-link["http://foo.com"]{link text}
'(a ((href "http://foo.com")(class "home-link")) "link text")))
(module+ test
(check-txexprs-equal? ◊image["pic.gif"]
'(img ((style "width: 100%") (class "bordered")(src "images/pic.gif"))))
(check-txexprs-equal? ◊image[#:border #f "pic.gif"]
'(img ((style "width: 100%")(src "images/pic.gif"))))
(check-txexprs-equal? ◊image[#:width "50%" "pic.gif"]
'(img ((style "width: 50%")(class "bordered")(src "images/pic.gif")))))
(module+ test
(check-txexprs-equal? ◊div-scale[.5]{Hello} '(div ((style "width: 0.5")) "Hello")))
(module+ test
(check-txexprs-equal? ◊font-scale[.75]{Hello}
'(span ((style "font-size: 0.75em")) "Hello")))
(module+ test
(check-txexprs-equal? ◊home-image["pic.gif"]
'(img ((style "width: 100%") (class "home-image") (src "images/pic.gif")))))
(module+ test
(check-txexprs-equal? ◊home-overlay["pic.gif"]{Hello}
'(div ((class "home-overlay") (style "background-image: url('pic.gif')"))
(div ((class "home-overlay-inner")) "Hello"))))
(module+ test
(check-txexprs-equal? ◊glyph{X}
'(span ((class "glyph")) "X"))
(check-txexprs-equal? ◊glyph[#:id "top"]{X}
'(span ((class "glyph")(id "top")) "X")))
(module+ test
(check-txexprs-equal? ◊image-wrapped{my-path}
'(img ((class "icon")
(style "width: 120px;")
(align "left")
(src "images/my-path")))))
(module+ test
(check-equal? (detect-list-items '("foo" "\n" "bar")) ; linebreak, not list item break
'((li (p "foo" (br) "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "bar")) ; paragraph break, not list item break
'((li (p "foo") (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "\n" "bar")) ; list item break
'((li (p "foo")) (li (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n\n\n" "bar")) ; list item break, concatenated
'((li (p "foo")) (li (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "\n\n\n" "bar")) ; list item break
'((li (p "foo")) (li (p "bar")))))
(module+ test
(check-txexprs-equal? ◊bullet-list{foo} '(ul (li (p "foo"))))
(check-txexprs-equal? ◊numbered-list{foo} '(ol (li (p "foo")))))
(module+ test
(check-txexprs-equal? ◊btw{foo
bar}
'(ul ((class "btw"))
(div ((id "btw-title")) "by the way")
(li (p "foo"))
(li (p "bar")))))
(module+ test
(check-txexprs-equal? ◊xref{target}
`(a ((class "xref") (href "target.html") ,no-hyphens-attr) "target"))
(check-txexprs-equal? ◊xref["url"]{target}
`(a ((class "xref") (href "url") ,no-hyphens-attr) "target"))
(check-exn exn:fail:contract:arity? (λ _ (xref "url" "target" "spurious-third-argument"))))
(module+ test
(check-equal? (target->url "foo?") "foo.html")
(check-equal? (target->url "FOO") "foo.html")
(check-equal? (target->url "foé") "foe.html")
(check-equal? (target->url "Foreword Lengthy Title") "foreword.html")
(check-equal? (target->url "Table of Contents and Other Nonsense") "toc.html")
(check-equal? (target->url "Nonbreaking Space and Spaces") "nonbreaking-space-and-spaces.html"))
(module+ test
(check-txexprs-equal? ◊topic{foo}
'(h3 ((class "topic")) "foo"))
(check-txexprs-equal? ◊subhead{foo}
'(h3 ((class "subhead")) "foo"))
(check-txexprs-equal? ◊font-headline{foo}
'(h3 ((class "font-headline")) "foo"))
(check-txexprs-equal? ◊section{foo}
'(h2 ((class "section")) "foo"))
(check-txexprs-equal? ◊chapter{foo}
'(h1 ((class "chapter")) "foo")))

File diff suppressed because it is too large Load Diff

@ -217,68 +217,6 @@ it merges those items into the list (i.e., does not leave them as a sublist).
`(,tag ,attrs ,@elements))
'(div ((class "big")) "text"))
#|
UNIT TESTS
Testing, as always, is optional, but strongly recommended. Unit tests are little one-line tests that
prove your function does what it says. As you refactor and reorganize your code, your unit tests will
let you know if you broke anything.
You can make unit tests with the `rackunit` library. Though you can put your unit tests in a separate
source file, I generally prefer to put them close to the function that they're testing. (For details
on the testing functions used below, see the docs for `rackunit`)
The ideal way to do this is with a `test` submodule. The code in a `test` submodule will only be used
a) when you run the file in DrRacket or
b) when `raco test` runs the file.
Otherwise, it is ignored.
We'll use the `module+` syntax for this. As the name suggests, `module+` creates a submodule that
incorporates everything else already in the source file. Moreover, all of our `module+ test` blocks
will be combined into a single submodule.
|#
(module+ test
(require rackunit) ;; always include this at the start of the test submodule
;; We use `check-txexprs-equal?` rather than `check-equal?` because it's a little more lenient:
;; it allows the attributes of two txexprs to be in a different order,
;; yet still be considered equal (because ordering of attributes is not semantically significant).
(check-txexprs-equal? (link "http://foo.com" "link text")
'(a ((href "http://foo.com")) "link text"))
;; The last test was fine, but it can be even better if we use a Pollen-mode command on the left.
;; That way, we can directly compare the command as it appears in Pollen input
;; with how it appears in the output.
(check-txexprs-equal? ◊link["http://foo.com"]{link text}
'(a ((href "http://foo.com")) "link text"))
;; It's wise to test as many valid input situations as you can.
(check-txexprs-equal? ◊link["http://foo.com" #:class 'main]{link text}
'(a ((href "http://foo.com")(class "main")) "link text"))
(check-txexprs-equal? ◊link["http://foo.com"]
'(a ((href "http://foo.com")) "http://foo.com"))
;; Strictly speaking, you could also write the last Pollen command like so:
(check-txexprs-equal? ◊link{http://foo.com} '(a ((href "http://foo.com")) "http://foo.com"))
;; That's not wrong. But in the interests of code readability,
;; I like to reserve the curly brackets in a Pollen command
;; for material that I expect to see displayed in the output
;; (e.g., textual and other content),
;; and use the square brackets for the other arguments.
;; You can also check that errors arise when they should.
;; Note that when testing for exceptions, you need to wrap your test expression in a function
;; (so that its evaluation can be delayed, otherwise you'd get the error immediately.)
;; The `(λ _ expression)` notation is a simple way.
;; (The `_` is the idiomatic way to notate something that will be ignored, in this case arguments.)
(check-exn exn:fail? (λ _ ◊link[])) ; no arguments
(check-exn exn:fail? (λ _ ◊link[#:invalid-keyword 42])) ; invalid keyword argument
(check-exn exn:fail? (λ _ ◊link[#f]))) ; invalid argument
;; For the sake of brevity, I'm going to write just one test for the remaining functions.
;; But you're encouraged to add more tests (or break the existing ones and see what happens).
#|
@ -313,10 +251,7 @@ Is equivalent to:
(define (buy-book-link . text-args)
(apply link buy-url text-args))
(module+ test
;; notice that we use `buy-url` in our test result.
;; That way, if we change the value of `buy-url`, the test won't break.
(check-txexprs-equal? ◊buy-book-link{link text} `(a ((href ,buy-url)) "link text")))
#|
`buylink`: creates a link styled with the "buylink" class.
@ -328,17 +263,13 @@ whatever URL we get from the Pollen source. So we add a `url` argument.
(define (buylink url . text-args)
(apply link url #:class "buylink" text-args))
(module+ test
(check-txexprs-equal? ◊buylink["http://foo.com"]{link text}
'(a ((href "http://foo.com")(class "buylink")) "link text")))
(define (home-link url . text-args)
(apply link url #:class "home-link" text-args))
(module+ test
(check-txexprs-equal? ◊home-link["http://foo.com"]{link text}
'(a ((href "http://foo.com")(class "home-link")) "link text")))
#|
BTW we could also be let the rest argument capture the URL,
@ -373,13 +304,7 @@ raise an error, letting us know that we're misusing it.
img-tag))
(module+ test
(check-txexprs-equal? ◊image["pic.gif"]
'(img ((style "width: 100%") (class "bordered")(src "images/pic.gif"))))
(check-txexprs-equal? ◊image[#:border #f "pic.gif"]
'(img ((style "width: 100%")(src "images/pic.gif"))))
(check-txexprs-equal? ◊image[#:width "50%" "pic.gif"]
'(img ((style "width: 50%")(class "bordered")(src "images/pic.gif")))))
#|
@ -394,8 +319,7 @@ raise an error, letting us know that we're misusing it.
(define base (make-txexpr 'div null text-args))
(attr-set base 'style (format "width: ~a" factor)))
(module+ test
(check-txexprs-equal? ◊div-scale[.5]{Hello} '(div ((style "width: 0.5")) "Hello")))
#|
@ -409,9 +333,7 @@ raise an error, letting us know that we're misusing it.
(define base (make-txexpr 'span null text-args))
(attr-set base 'style (format "font-size: ~aem" ratio)))
(module+ test
(check-txexprs-equal? ◊font-scale[.75]{Hello}
'(span ((style "font-size: 0.75em")) "Hello")))
#|
@ -424,9 +346,7 @@ raise an error, letting us know that we're misusing it.
(define (home-image image-path)
(attr-set (image image-path) 'class "home-image"))
(module+ test
(check-txexprs-equal? ◊home-image["pic.gif"]
'(img ((style "width: 100%") (class "home-image") (src "images/pic.gif")))))
#|
@ -443,10 +363,7 @@ It also makes it possible to change the fiddly HTML markup from one central loca
`(div ((class "home-overlay")(style ,(format "background-image: url('~a')" img-path)))
(div ((class "home-overlay-inner")) ,@text-args)))
(module+ test
(check-txexprs-equal? ◊home-overlay["pic.gif"]{Hello}
'(div ((class "home-overlay") (style "background-image: url('pic.gif')"))
(div ((class "home-overlay-inner")) "Hello"))))
#|
@ -460,11 +377,7 @@ Any keywords passed in will be propagated to every use of the tag function.
|#
(define glyph (make-default-tag-function 'span #:class "glyph"))
(module+ test
(check-txexprs-equal? ◊glyph{X}
'(span ((class "glyph")) "X"))
(check-txexprs-equal? ◊glyph[#:id "top"]{X}
'(span ((class "glyph")(id "top")) "X")))
#|
@ -477,12 +390,7 @@ Any keywords passed in will be propagated to every use of the tag function.
(define (image-wrapped img-path)
(attr-set* (image img-path) 'class "icon" 'style "width: 120px;" 'align "left"))
(module+ test
(check-txexprs-equal? ◊image-wrapped{my-path}
'(img ((class "icon")
(style "width: 120px;")
(align "left")
(src "images/my-path")))))
#|
@ -530,17 +438,7 @@ For simplicity, I'm not using them here.
(define li-tag (make-default-tag-function 'li))
(map (λ(lip) (apply li-tag lip)) list-of-li-paragraphs))
(module+ test
(check-equal? (detect-list-items '("foo" "\n" "bar")) ; linebreak, not list item break
'((li (p "foo" (br) "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "bar")) ; paragraph break, not list item break
'((li (p "foo") (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "\n" "bar")) ; list item break
'((li (p "foo")) (li (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n\n\n" "bar")) ; list item break, concatenated
'((li (p "foo")) (li (p "bar"))))
(check-equal? (detect-list-items '("foo" "\n" "\n" "\n\n\n" "bar")) ; list item break
'((li (p "foo")) (li (p "bar")))))
#|
@ -578,9 +476,7 @@ Now we can define `bullet-list` and `numbered-list` using our helper function.
(define bullet-list (make-list-function 'ul))
(define numbered-list (make-list-function 'ol))
(module+ test
(check-txexprs-equal? ◊bullet-list{foo} '(ul (li (p "foo"))))
(check-txexprs-equal? ◊numbered-list{foo} '(ol (li (p "foo")))))
#|
@ -601,16 +497,8 @@ The `btw` tag expands to an HTML list, which we will then crack open and add a h
'(div ((id "btw-title")) "by the way")
(get-elements btw-list)))
(module+ test
(check-txexprs-equal? ◊btw{foo
bar}
'(ul ((class "btw"))
(div ((id "btw-title")) "by the way")
(li (p "foo"))
(li (p "bar")))))
#|
`xref`: create a styled cross-reference link, with optional destination argument.
@ -639,12 +527,7 @@ But to be safe, we'll raise an arity error if we get too many arguments.
[more-than-two-args (apply raise-arity-error 'xref (list 1 2) more-than-two-args)]))
(module+ test
(check-txexprs-equal? ◊xref{target}
`(a ((class "xref") (href "target.html") ,no-hyphens-attr) "target"))
(check-txexprs-equal? ◊xref["url"]{target}
`(a ((class "xref") (href "url") ,no-hyphens-attr) "target"))
(check-exn exn:fail:contract:arity? (λ _ (xref "url" "target" "spurious-third-argument"))))
#|
@ -670,13 +553,7 @@ The name of the source file for a page is determined by its title.
[xn (string-replace xn " " "-")]) ; replace word space with hyphen
(format "~a.html" xn)))
(module+ test
(check-equal? (target->url "foo?") "foo.html")
(check-equal? (target->url "FOO") "foo.html")
(check-equal? (target->url "foé") "foe.html")
(check-equal? (target->url "Foreword Lengthy Title") "foreword.html")
(check-equal? (target->url "Table of Contents and Other Nonsense") "toc.html")
(check-equal? (target->url "Nonbreaking Space and Spaces") "nonbreaking-space-and-spaces.html"))
#|
`xref-font`: special version of `xref` for the fontrec directory
@ -715,17 +592,7 @@ with arguments that will be filled in when you invoke the macro.
(define-heading section 'h2)
(define-heading chapter 'h1)
(module+ test
(check-txexprs-equal? ◊topic{foo}
'(h3 ((class "topic")) "foo"))
(check-txexprs-equal? ◊subhead{foo}
'(h3 ((class "subhead")) "foo"))
(check-txexprs-equal? ◊font-headline{foo}
'(h3 ((class "font-headline")) "foo"))
(check-txexprs-equal? ◊section{foo}
'(h2 ((class "section")) "foo"))
(check-txexprs-equal? ◊chapter{foo}
'(h1 ((class "chapter")) "foo")))
#|
`define-heading-from-metas`: macro for defining a function that makes a heading