# encoding=utf-8 require 'spec_helper' describe Polytexnic::Pipeline do before(:all) do FileUtils.rm('.highlight_cache') if File.exist?('.highlight_cache') end describe '#to_polytex' do subject(:processed_text) do Polytexnic::Pipeline.new(source, source: :markdown).polytex end context "for vanilla Markdown" do let(:source) { '*foo* **bar**' } it { should include '\emph{foo} \textbf{bar}' } it { should_not include '\begin{document}' } end context "with a manual break" do let(:source) { '[Michael Hartl](http://www.michaelhartl.com/) \\\\ Author, [*The Ruby on Rails Tutorial*](http://railstutorial.org/)' } it { should include '\\\\' } it { should_not include '\textbackslash' } end context "for multiline Markdown" do let(:source) do <<-EOS # A chapter Hello, *world*! ## A section Lorem ipsum EOS end it { should include '\chapter{A chapter}' } it { should include '\section{A section}' } it { should_not include '\hypertarget' } end context "hyphenation" do let(:source) { 'profes\-sional' } it { should include source } end context "links" do context "with normal text" do let(:source) { '[foo](http://example.com/)' } it { should include '\href{http://example.com/}{foo}' } end context "with a percent" do let(:source) { '[foo bar](http://example.com/foo%20bar)' } it { should include '\href{http://example.com/foo%20bar}{foo bar}' } end end describe "with math" do context "inline math" do let(:source) do <<-'EOS' This is inline math: {$$} x^2 {/$$}. EOS end it { should include '\( x^2 \)' } end context "block math" do let(:source) do <<-'EOS' This is block math: {$$} x^2 {/$$}. EOS end it { should resemble '\[x^2\]' } end end context "asides with internal lists" do let(:source) do <<-'EOS' \begin{aside} \label{aside:softcover_uses} \heading{How to use Softcover} * Producing ebooks with `softcover` and giving them away * Producing ebooks with `softcover` and selling them from your own website \end{aside} EOS end it { should include '\begin{aside}' } it { should_not include "\\end{aside}\n\\end{itemize}" } end describe "tables" do let(:source) do <<-'EOS' \begin{table} |**option**|**size**|**actual size**| | k | kilobytes | (1024 bytes) | | M | megabytes | (1024 kilobytes) | | G | gigabytes | (1024 megabytes) | | T | terabytes | (1024 gigabytes) | | P | petabytes | (1024 terabytes) | \end{table} \begin{table} |**option**|**size**|**actual size**| | k | kilobytes | (1024 bytes) | | M | megabytes | (1024 kilobytes) | | G | gigabytes | (1024 megabytes) | | T | terabytes | (1024 gigabytes) | | P | petabytes | (1024 terabytes) | \caption{A caption.} \end{table} EOS end it { should include '\begin{table}' } it { should include '\begin{longtable}' } it { should_not include '\textbar' } end describe "footnotes" do subject do Polytexnic::Pipeline.new(markdown, source: :markdown).polytex end context "first chapter with footnotes" do let(:markdown) do <<-'EOS' To add a footnote, you insert a footnote tag like this.[^foo] Then you add the footnote content later in the text, using the same tag, with a colon and a space:[^foo2] [^foo]: This is the footnote content. That is it. You can keep writing your text after the footnote content. [^foo2]: This is the footnote text. We are now going to add a second line after typing in four spaces. EOS end it { should include '\footnote{This is the footnote content.}' } it { should include 'after typing in four spaces.}' } end end describe "images" do subject do Polytexnic::Pipeline.new(markdown, source: :markdown).polytex end context "inline" do let(:markdown) { 'Inline ![Caption](img.png) image' } it { should include '\includegraphics{img.png}' } it { should_not include '\image' } end context "with only a label" do let(:markdown) do <<-'EOS' ![\label{fig:softcover_server}](images/figures/softcover_server.png) EOS end it { should include '\begin{figure}[H]' } it { should include '\caption' } end context "with alt text but no label" do let(:markdown) do <<-'EOS' ![Running the Softcover server in a separate tab.](images/figures/softcover_server.png) EOS end it { should_not include '\begin{figure}' } it { should_not include '\caption' } it { should include '\image' } end context "with a caption and a label" do let(:markdown) do <<-'EOS' ![Running the Softcover server in a separate tab.\label{fig:softcover_server}](images/figures/softcover_server.png) EOS end it { should include '\caption{Running the Softcover server in a separate tab.\label{fig:softcover_server}}' } it { should include '\image' } it { should_not include '\includegraphics' } end context "using an example that failed" do let(:markdown) do <<-'EOS' a screenshot from [Lowdown](http://lowdownapp.com/), a web application that developers use for organizing user stories. ![Lowdown for user stories\label{fig:lowdown}](https://tutorials.railsapps.org/assets/learn-rails-lowdown-partial.png) Just like Rails provides a structure for building a web application, user stories provide a structure for organizing your product plan. EOS end it { should include '\caption{Lowdown for user stories' } it { should include '\image{https://tutorials.railsapps.org' } end end context "with LaTeX containing" do context "a normal command" do let(:source) { 'This is a command: \foobar' } it { should include source } end context "backslash space" do let(:source) { 'Dr.\ No' } it { should include source } end context "escaped special characters" do let(:source) { '\% \& \$ \# \@ \_' } it { should include source } end context "an accented character" do let(:source) { "\\`{e}" } it { should include source } end context "a label and cross-reference" do let(:source) do <<-'EOS' # Chapter One \label{cha:one} Chapter~\ref{cha:one} EOS end it { should include '\label{cha:one}' } it { should include 'Chapter~\ref{cha:one}' } end context "inline math" do let(:source) { '\( x \) is a variable' } it { should include source } end context "inline math with a newline" do let(:source) { '\( x' + "\n" + ' + y \) is a sum' } it { should include source } end context "an inline math with no spaces" do let(:source) { '\(\mathbb{R}\)'} it { should include source } end context "a centered equation" do let(:source) { '\[ x^2 - 2 = 0 \] is an equation' } it { should resemble source } end context "a centered equation with a newline" do let(:source) do <<-'EOS' \[ \left(\frac{p}{q}\right) \left(\frac{q}{p}\right) = (-1)^{[(p-1)/2][(q-1)/2]} \quad\text{($p$, $q$ distinct odd primes)} \] EOS end it { should include source.chomp } end context "an equation environment" do let(:source) do <<-'EOS' foo \begin{equation} \label{eq:maxwell} \left.\begin{aligned} \nabla\cdot\mathbf{E} & = \rho \\ \nabla\cdot\mathbf{B} & = 0 \\ \nabla\times\mathbf{E} & = -\dot{\mathbf{B}} \\ \nabla\times\mathbf{B} & = \mathbf{J} + \dot{\mathbf{E}} \end{aligned} \right\} \quad\text{Maxwell equations} \end{equation} bar EOS end it { should resemble source } end context "a codelisting environment, including a nested command" do let(:source) do <<-'EOS' \begin{codelisting} \codecaption{Lorem \emph{ipsum}.} \label{code:lorem} ```ruby def foo; "bar"; end ``` \end{codelisting} EOS end it { should resemble '\begin{codelisting}' } it { should resemble '\codecaption{Lorem \emph{ipsum}.}' } it { should resemble '\label{code:lorem}' } it { should resemble '\end{codelisting}' } end context "a commented-out codelisting" do let(:source) do <<-'EOS' %= foo:bar EOS end it { should include '%= foo:bar' } it { should_not match /^\\begin\{code\}/ } end context "code inclusion inside codelisting" do let(:source) do <<-'EOS' \begin{codelisting} \codecaption{Lorem ipsum.} \label{code:lorem} <<(/path/to/code) \end{codelisting} EOS end it { should resemble '%= <<(/path/to/code)' } end context "codelisting followed by a section" do let(:source) do <<-'EOS' \begin{codelisting} \codecaption{Lorem ipsum.} \label{code:lorem} <<(/path/to/code) \end{codelisting} # Foo EOS end it { should resemble '\chapter{Foo}' } end context "a tabular LaTeX environment" do let(:source) do <<-'EOS' \begin{tabularx} a & b \\ c & d \end{tabularx} EOS end it { should resemble source } end context "a raw longtable environment" do let(:source) do <<-'EOS' \begin{longtable}{|c|c|} a & b \\ c & d \\ \hline \caption{Foo bar.} \end{longtable} EOS end it { should resemble source } end end describe "source code" do context "inline" do let(:source) { '`foo bar`' } it { should include '\kode{foo bar}' } end context "inline with a newline" do let(:source) { "`foo\nbar`" } it { should include "\\kode{foo\nbar}" } end context "without highlighting" do let(:source) do <<-EOS def foo "bar" end EOS end let(:output) do <<-'EOS' \begin{verbatim} def foo "bar" end \end{verbatim} EOS end it { should resemble output } end context "with highlighting" do let(:source) do <<-EOS {lang="ruby"} def foo "bar" end lorem EOS end let(:output) do <<-'EOS' %= lang:ruby \begin{code} def foo "bar" end \end{code} lorem EOS end it { should resemble output } end describe "code inclusion" do let(:source) { '<<(/path/to/code)' } it { should resemble '%= <<(/path/to/code)' } context "with an alternate lang and options" do let(:source) { '<<(/path/to/code.md, lang: text, options: "hl_lines": [1, 2], "linenos": true)' } it { should resemble '%= <<(/path/to/code.md, lang: text, options: "hl_lines": [1, 2], "linenos": true)' } end end describe "GitHub-flavored code fencing" do context "without highlighting" do let(:source) do <<-EOS ``` def foo "bar" end ``` lorem EOS end let(:output) do <<-'EOS' %= lang:text \begin{code} def foo "bar" end \end{code} lorem EOS end it { should resemble output } end context "with a compound language" do let(:source) do <<-EOS # Softcover-flavored Markdown HTML and PHP: ```html+php Name: ``` As a final enhancement EOS end let(:output) do <<-'EOS' %= lang:html+php \begin{code} Name: \end{code} EOS end it { should resemble output } end context "with highlighting and options" do let(:source) do <<-EOS ```ruby, options: "hl_lines": [1, 2], "linenos": true def foo "bar" end ``` lorem EOS end let(:output) do <<-'EOS' %= lang:ruby, options: "hl_lines": [1, 2], "linenos": true \begin{code} def foo "bar" end \end{code} lorem EOS end it { should resemble output } end context "with a code listing from Urbit that broke" do let(:source) do <<-'EOS' ```text 'Foo \'bar' 0x27 ``` foo EOS end it { should_not include "\\begin{code}\n'Foo \n"} end context "with nested fencing" do let(:source) do <<-'EOS' ```text ```ruby, options: "hl_lines": [1, 2], "linenos": true def hello; puts 'hello'; end ``` ``` EOS end let(:output) do <<-'EOS' %= lang:text \begin{code} ```ruby, options: "hl_lines": [1, 2], "linenos": true def hello; puts 'hello'; end ``` \end{code} EOS end it { should resemble output } end end end describe '\input command' do let(:external_file) { 'foo.md' } let(:nested_external_file) { 'bar.md' } let(:input) do <<-'EOS' Lorem ipsum ```ruby def foo; 'foo'; end ``` Lorem *ipsum* dolor sit amet \input{bar} EOS end let(:nested_input) do <<-'EOS' Lorem ipsum ```python def bar(): return "bar" ``` EOS end before do File.write(external_file, input) File.write(nested_external_file, nested_input) end after do File.unlink(external_file) File.unlink(nested_external_file) end let(:output) do Polytexnic::Pipeline.new(input, source: :markdown).polytex end let(:source) { "# Foo\n\n \\input{foo} " } it { should include output } end end end