├── .gitignore ├── LICENSE ├── ruby_haml_test.rb ├── lua_haml_spec.lua ├── python_haml_test.py ├── perl_haml_test.pl ├── README.md └── tests.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /ruby_haml_test.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "minitest/autorun" 3 | require "json" 4 | require "haml" 5 | 6 | class HamlTest < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Unit::TestCase) 7 | contexts = JSON.parse(File.read(File.dirname(__FILE__) + "/tests.json")) 8 | contexts.each do |context| 9 | context[1].each do |name, test| 10 | define_method("test_spec: #{name} (#{context[0]})") do 11 | html = test["html"] 12 | haml = test["haml"] 13 | locals = Hash[(test["locals"] || {}).map {|x, y| [x.to_sym, y]}] 14 | options = Hash[(test["config"] || {}).map {|x, y| [x.to_sym, y]}] 15 | options[:format] = options[:format].to_sym if options.key?(:format) 16 | engine = Haml::Engine.new(haml, options) 17 | result = engine.render(Object.new, locals) 18 | 19 | assert_equal html, result.strip 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lua_haml_spec.lua: -------------------------------------------------------------------------------- 1 | local dir = require 'pl.dir' 2 | local haml = require 'haml' 3 | local json = require 'json' 4 | local path = require 'pl.path' 5 | local telescope = require 'telescope' 6 | local assert = assert 7 | local describe = telescope.describe 8 | local getinfo = debug.getinfo 9 | local it = telescope.it 10 | local open = io.open 11 | local pairs = pairs 12 | 13 | module('hamlspec') 14 | 15 | local function get_tests(filename) 16 | local me = path.abspath(getinfo(1).source:match("@(.*)$")) 17 | return path.join(path.dirname(me), filename) 18 | end 19 | 20 | local json_file = get_tests("tests.json") 21 | local file = assert(open(json_file)) 22 | local input = file:read '*a' 23 | file:close() 24 | 25 | local contexts = json.decode(input) 26 | 27 | describe("LuaHaml", function() 28 | for context, expectations in pairs(contexts) do 29 | describe("When handling " .. context, function() 30 | for name, exp in pairs(expectations) do 31 | it(("should correctly render %s"):format(name), function() 32 | local engine = haml.new(exp.config) 33 | assert_equal(engine:render(exp.haml, exp.locals), exp.html) 34 | end) 35 | end 36 | end) 37 | end 38 | end) -------------------------------------------------------------------------------- /python_haml_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, unicode_literals 2 | 3 | import json 4 | import regex 5 | 6 | from collections import OrderedDict 7 | from hamlpy import compiler 8 | 9 | 10 | num_pass, num_fail, num_error = 0, 0, 0 11 | 12 | DJANGO_VARIABLE_REGEX = regex.compile('{{([^}]+)}}') 13 | 14 | 15 | def compile_haml(test): 16 | """ 17 | Transform the given Haml into a Django template and perform variable substitution 18 | """ 19 | dj_html = compiler.Compiler(options=test.get('config')).process(test['haml']).strip() 20 | 21 | def var_sub(match): 22 | return test.get('locals', {}).get(match.group(1).strip()) 23 | 24 | # perform substitution of Django style {{ ... }} variable tags 25 | return DJANGO_VARIABLE_REGEX.sub(var_sub, dj_html) 26 | 27 | 28 | with open('tests.json') as f: 29 | tests = json.load(f, object_pairs_hook=OrderedDict) 30 | for category, category_tests in tests.items(): 31 | print(category) 32 | for description, test in category_tests.items(): 33 | try: 34 | html = compile_haml(test) 35 | if html == test['html']: 36 | print(" > %s: OK" % description) 37 | num_pass += 1 38 | else: 39 | print(" > %s: FAIL" % description) 40 | print(" - Expected: %s" % test['html'].replace('\n', '\\n')) 41 | print(" - Actual : %s" % html.replace('\n', '\\n')) 42 | num_fail += 1 43 | except Exception as e: 44 | print(" > %s: ERROR" % description) 45 | print(" - Exception: %s" % str(e)) 46 | num_error += 1 47 | 48 | print("==== Passed: %d, Failed: %d, Errors: %d ====" % (num_pass, num_fail, num_error)) 49 | -------------------------------------------------------------------------------- /perl_haml_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use Text::Haml; 8 | use FindBin; 9 | use JSON 'from_json'; 10 | 11 | our $VERSION = 0.990101; 12 | 13 | my $tests; 14 | 15 | open FILE, "< $FindBin::Bin/tests.json" or die $!; 16 | $tests = from_json(join("\n", )); 17 | close FILE; 18 | 19 | 20 | my $test_count = 0; 21 | while (my ($section_name, $section) = each %$tests) { 22 | $test_count += scalar(keys(%$section)); 23 | } 24 | 25 | plan tests => $test_count; 26 | 27 | # want the test numbers to match across runs 28 | for my $section_name (sort keys(%$tests)) { 29 | my $section = $tests->{$section_name}; 30 | 31 | diag $section_name; 32 | 33 | for my $test_name (sort keys(%$section)) { 34 | my $test = $section->{$test_name}; 35 | is( Text::Haml->new(%{$test->{config}}, vars_as_subs => 1) 36 | ->render($test->{haml}, %{$test->{locals}}), 37 | $test->{html}, $test_name 38 | ); 39 | } 40 | } 41 | __END__ 42 | 43 | =head1 NAME 44 | 45 | perl_haml_test.pl - Text::Haml spec tests runner 46 | 47 | =head1 SYNOPSIS 48 | 49 | $ perl perl_haml_test.pl 50 | 51 | # conditional comments 52 | ok 1 - a conditional comment 53 | # tags with nested content 54 | ok 2 - a tag with CSS 55 | 56 | ... 57 | 58 | ok 81 - an inline comment 59 | ok 82 - a nested comment 60 | 1..82 61 | 62 | =head1 DESCRIPTION 63 | 64 | This file is a part of Haml spec tests envorinment. It tests Perl 65 | implementation using . 66 | 67 | =head1 DEPENDENCIES 68 | 69 | =over 70 | 71 | * Text::Haml (available via CPAN or http://github.com/vti/text-haml) 72 | * JSON (available on CPAN) 73 | * Test::More (included in Perl core) 74 | * FindBin (included in Perl core) 75 | 76 | =back 77 | 78 | =head1 SEE ALSO 79 | 80 | L 81 | 82 | =head1 AUTHOR 83 | 84 | Viacheslav Tykhanovskyi, C. 85 | 86 | =head1 COPYRIGHT AND LICENSE 87 | 88 | Copyright (C) 2009, Viacheslav Tykhanovskyi 89 | 90 | This program is free software, you can redistribute it and/or modify it under 91 | the terms of the Artistic License version 2.0. 92 | 93 | =cut 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Haml Spec # 2 | 3 | Haml Spec provides a basic suite of tests for Haml interpreters. 4 | 5 | It is intented for developers who are creating or maintaining an implementation 6 | of the [Haml](http://haml-lang.com) markup language. 7 | 8 | At the moment, there are test runners for the [original 9 | Haml](http://github.com/nex3/haml) in Ruby, [Lua 10 | Haml](http://github.com/norman/lua-haml) and the 11 | [Text::Haml](http://github.com/vti/text-haml) Perl port. Support for other 12 | versions of Haml will be added if their developers/maintainers are interested in 13 | using it. 14 | 15 | ## The Tests ## 16 | 17 | The tests are kept in JSON format for portability across languages. Each test 18 | is a JSON object with expected input, output, local variables and configuration 19 | parameters (see below). The test suite only provides tests for features which 20 | are portable, therefore no tests for script are provided, nor for external 21 | filters such as :markdown or :textile. 22 | 23 | The one major exception to this are the tests for interpolation, which you may 24 | need to modify with a regular expression to run under PHP or Perl, which 25 | require a sigil before variable names. These tests are included despite being 26 | less than 100% portable because interpolation is an important part of Haml and 27 | can be tricky to implement. These tests are flagged as "optional" so that you 28 | can avoid running them if your implementation of Haml will not support this 29 | feature. 30 | 31 | ## Running the Tests ## 32 | 33 | ### Ruby ### 34 | 35 | The Ruby test runner uses minitest, the same as the Ruby Haml implementation. 36 | To run the tests you probably only need to install `haml`, `minitest` and 37 | possibly `ruby` if your platform doesn't come with it by default. If you're 38 | using Ruby 1.8.x, you'll also need to install `json`: 39 | 40 | sudo gem install haml 41 | sudo gem install minitest 42 | # for Ruby 1.8.x; check using "ruby --version" if unsure 43 | sudo gem install json 44 | 45 | Then, running the Ruby test suite is easy: 46 | 47 | ruby ruby_haml_test.rb 48 | 49 | At the moment, running the tests with Ruby 1.8.7 fails because of issues with 50 | the JSON library. Please use 1.9.2 until this is resolved. 51 | 52 | ### Lua ### 53 | 54 | The Lua test depends on 55 | [Penlight](http://stevedonovan.github.com/Penlight/), 56 | [Telescope](http://github.com/norman/telescope), 57 | [jason4lua](http://json.luaforge.net/), and 58 | [Lua Haml](http://github.com/norman/lua-haml). Install and run `tsc 59 | lua_haml_spec.lua`. 60 | 61 | ### Python/Django ### 62 | 63 | The Python tests use [django-hamlpy](https://github.com/nyaruka/django-hamlpy) 64 | implementation. To install and the run the tests: 65 | 66 | pip install django-hamlpy 67 | python python_haml_test.py 68 | 69 | ### Getting it ### 70 | 71 | You can access the [Git repository](http://github.com/norman/haml-spec) at: 72 | 73 | git://github.com/norman/haml-spec.git 74 | 75 | Patches are *very* welcome, as are test runners for your Haml implementation. 76 | 77 | As long as any test you add run against Ruby Haml and are not redundant, I'll 78 | be very happy to add them. 79 | 80 | ### Test JSON format ### 81 | 82 | "test name" : { 83 | "haml" : "haml input", 84 | "html" : "expected html output", 85 | "result" : "expected test result", 86 | "locals" : "local vars", 87 | "config" : "config params", 88 | "optional" : true|false 89 | } 90 | 91 | * test name: This should be a *very* brief description of what's being tested. It can 92 | be used by the test runners to name test methods, or to exclude certain tests from being 93 | run. 94 | * haml: The Haml code to be evaluated. Always required. 95 | * html: The HTML output that should be generated. Required unless "result" is "error". 96 | * result: Can be "pass" or "error". If it's absent, then "pass" is assumed. If it's "error", 97 | then the goal of the test is to make sure that malformed Haml code generates an error. 98 | * locals: An object containing local variables needed for the test. 99 | * config: An object containing configuration parameters used to run the test. 100 | The configuration parameters should be usable directly by Ruby's Haml with no 101 | modification. If your implementation uses config parameters with different 102 | names, you may need to process them to make them match your implementation. 103 | If your implementation has options that do not exist in Ruby's Haml, then you 104 | should add tests for this in your implementation's test rather than here. 105 | * optional: whether or not the test is optional 106 | 107 | ## License ## 108 | 109 | This project is released under the [WTFPL](http://sam.zoy.org/wtfpl/) in order 110 | to be as usable as possible in any project, commercial or free. 111 | 112 | ## Author ## 113 | 114 | [Norman Clarke](mailto:norman@njclarke.com) 115 | -------------------------------------------------------------------------------- /tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers" : { 3 | 4 | "an XHTML XML prolog" : { 5 | "haml" : "!!! XML", 6 | "html" : "", 7 | "config" : { 8 | "format" : "xhtml" 9 | } 10 | }, 11 | 12 | "an XHTML default (transitional) doctype" : { 13 | "haml" : "!!!", 14 | "html" : "", 15 | "config" : { 16 | "format" : "xhtml" 17 | } 18 | }, 19 | 20 | "an XHTML 1.1 doctype" : { 21 | "haml" : "!!! 1.1", 22 | "html" : "", 23 | "config" : { 24 | "format" : "xhtml" 25 | } 26 | }, 27 | 28 | "an XHTML 1.2 mobile doctype" : { 29 | "haml" : "!!! mobile", 30 | "html" : "", 31 | "config" : { 32 | "format" : "xhtml" 33 | } 34 | }, 35 | 36 | "an XHTML 1.1 basic doctype" : { 37 | "haml" : "!!! basic", 38 | "html" : "", 39 | "config" : { 40 | "format" : "xhtml" 41 | } 42 | }, 43 | 44 | "an XHTML 1.0 frameset doctype" : { 45 | "haml" : "!!! frameset", 46 | "html" : "", 47 | "config" : { 48 | "format" : "xhtml" 49 | } 50 | }, 51 | 52 | "an HTML 5 doctype with XHTML syntax" : { 53 | "haml" : "!!! 5", 54 | "html" : "", 55 | "config" : { 56 | "format" : "xhtml" 57 | } 58 | }, 59 | 60 | "an HTML 5 XML prolog (silent)" : { 61 | "haml" : "!!! XML", 62 | "html" : "", 63 | "config" : { 64 | "format" : "html5" 65 | } 66 | }, 67 | 68 | "an HTML 5 doctype" : { 69 | "haml" : "!!!", 70 | "html" : "", 71 | "config" : { 72 | "format" : "html5" 73 | } 74 | }, 75 | 76 | "an HTML 4 XML prolog (silent)" : { 77 | "haml" : "!!! XML", 78 | "html" : "", 79 | "config" : { 80 | "format" : "html4" 81 | } 82 | }, 83 | 84 | "an HTML 4 default (transitional) doctype" : { 85 | "haml" : "!!!", 86 | "html" : "", 87 | "config" : { 88 | "format" : "html4" 89 | } 90 | }, 91 | 92 | "an HTML 4 frameset doctype" : { 93 | "haml" : "!!! frameset", 94 | "html" : "", 95 | "config" : { 96 | "format" : "html4" 97 | } 98 | }, 99 | 100 | "an HTML 4 strict doctype" : { 101 | "haml" : "!!! strict", 102 | "html" : "", 103 | "config" : { 104 | "format" : "html4" 105 | } 106 | } 107 | 108 | }, 109 | 110 | "basic Haml tags and CSS": { 111 | 112 | "a simple Haml tag" : { 113 | "haml" : "%p", 114 | "html" : "

" 115 | }, 116 | 117 | "a self-closing tag (XHTML)" : { 118 | "haml" : "%meta", 119 | "html" : "", 120 | "config" : { 121 | "format" : "xhtml" 122 | } 123 | }, 124 | 125 | "a self-closing tag (HTML4)" : { 126 | "haml" : "%meta", 127 | "html" : "", 128 | "config" : { 129 | "format" : "html4" 130 | } 131 | }, 132 | 133 | "a self-closing tag (HTML5)" : { 134 | "haml" : "%meta", 135 | "html" : "", 136 | "config" : { 137 | "format" : "html5" 138 | } 139 | }, 140 | 141 | "a self-closing tag ('/' modifier + XHTML)" : { 142 | "haml" : "%zzz/", 143 | "html" : "", 144 | "config" : { 145 | "format" : "xhtml" 146 | } 147 | }, 148 | 149 | "a self-closing tag ('/' modifier + HTML5)" : { 150 | "haml" : "%zzz/", 151 | "html" : "", 152 | "config" : { 153 | "format" : "html5" 154 | } 155 | }, 156 | 157 | "a tag with a CSS class" : { 158 | "haml" : "%p.class1", 159 | "html" : "

" 160 | }, 161 | 162 | "a tag with multiple CSS classes" : { 163 | "haml" : "%p.class1.class2", 164 | "html" : "

" 165 | }, 166 | 167 | "a tag with a CSS id" : { 168 | "haml" : "%p#id1", 169 | "html" : "

" 170 | }, 171 | 172 | "a tag with multiple CSS id's" : { 173 | "haml" : "%p#id1#id2", 174 | "html" : "

" 175 | }, 176 | 177 | "a tag with a class followed by an id" : { 178 | "haml" : "%p.class1#id1", 179 | "html" : "

" 180 | }, 181 | 182 | "a tag with an id followed by a class" : { 183 | "haml" : "%p#id1.class1", 184 | "html" : "

" 185 | }, 186 | 187 | "an implicit div with a CSS id" : { 188 | "haml" : "#id1", 189 | "html" : "
" 190 | }, 191 | 192 | "an implicit div with a CSS class" : { 193 | "haml" : ".class1", 194 | "html" : "
" 195 | }, 196 | 197 | "multiple simple Haml tags" : { 198 | "haml" : "%div\n %div\n %p", 199 | "html" : "
\n
\n

\n
\n
" 200 | } 201 | }, 202 | 203 | "tags with unusual HTML characters" : { 204 | 205 | "a tag with colons" : { 206 | "haml" : "%ns:tag", 207 | "html" : "" 208 | }, 209 | 210 | "a tag with underscores" : { 211 | "haml" : "%snake_case", 212 | "html" : "" 213 | }, 214 | 215 | "a tag with dashes" : { 216 | "haml" : "%dashed-tag", 217 | "html" : "" 218 | }, 219 | 220 | "a tag with camelCase" : { 221 | "haml" : "%camelCase", 222 | "html" : "" 223 | }, 224 | 225 | "a tag with PascalCase" : { 226 | "haml" : "%PascalCase", 227 | "html" : "" 228 | } 229 | }, 230 | 231 | "tags with unusual CSS identifiers" : { 232 | 233 | "an all-numeric class" : { 234 | "haml" : ".123", 235 | "html" : "
" 236 | }, 237 | 238 | "a class with underscores" : { 239 | "haml" : ".__", 240 | "html" : "
" 241 | }, 242 | 243 | "a class with dashes" : { 244 | "haml" : ".--", 245 | "html" : "
" 246 | }, 247 | 248 | "a class with escaped unsupported characters like / and '\\'": { 249 | "haml" : ".h\/c\\", 250 | "html" : "
" 251 | } 252 | }, 253 | 254 | "tags with inline content" : { 255 | 256 | "Inline content simple tag" : { 257 | "haml" : "%p hello", 258 | "html" : "

hello

" 259 | }, 260 | 261 | "Inline content tag with CSS" : { 262 | "haml" : "%p.class1 hello", 263 | "html" : "

hello

" 264 | }, 265 | 266 | "Inline content multiple simple tags" : { 267 | "haml" : "%div\n %div\n %p text", 268 | "html" : "
\n
\n

text

\n
\n
" 269 | } 270 | }, 271 | 272 | "tags with nested content" : { 273 | 274 | "Nested content simple tag" : { 275 | "haml" : "%p\n hello", 276 | "html" : "

\nhello\n

" 277 | }, 278 | 279 | "Nested content tag with CSS" : { 280 | "haml" : "%p.class1\n hello", 281 | "html" : "

\nhello\n

" 282 | }, 283 | 284 | "Nested content multiple simple tags" : { 285 | "haml" : "%div\n %div\n %p\n text", 286 | "html" : "
\n
\n

\ntext\n

\n
\n
" 287 | } 288 | }, 289 | 290 | "tags with HTML-style attributes": { 291 | 292 | "HTML-style one attribute" : { 293 | "haml" : "%p(a='b')", 294 | "html" : "

" 295 | }, 296 | 297 | "HTML-style multiple attributes" : { 298 | "haml" : "%p(a='b' c='d')", 299 | "html" : "

" 300 | }, 301 | 302 | "HTML-style attributes separated with newlines" : { 303 | "haml" : "%p(a='b'\n c='d')", 304 | "html" : "

" 305 | }, 306 | 307 | "HTML-style interpolated attribute" : { 308 | "haml" : "%p(a=\"#{var}\")", 309 | "html" : "

", 310 | "locals" : { 311 | "var" : "value" 312 | } 313 | }, 314 | 315 | "HTML-style 'class' as an attribute" : { 316 | "haml" : "%p(class='class1')", 317 | "html" : "

" 318 | }, 319 | 320 | "HTML-style tag with a CSS class and 'class' as an attribute" : { 321 | "haml" : "%p.class2(class='class1')", 322 | "html" : "

" 323 | }, 324 | 325 | "HTML-style tag with 'id' as an attribute" : { 326 | "haml" : "%p(id='1')", 327 | "html" : "

" 328 | }, 329 | 330 | "HTML-style tag with a CSS id and 'id' as an attribute" : { 331 | "haml" : "%p#id(id='1')", 332 | "html" : "

" 333 | }, 334 | 335 | "HTML-style tag with a variable attribute" : { 336 | "haml" : "%p(class=var)", 337 | "html" : "

", 338 | "locals" : { 339 | "var" : "hello" 340 | } 341 | }, 342 | 343 | "HTML-style tag with a CSS class and 'class' as a variable attribute" : { 344 | "haml" : ".hello(class=var)", 345 | "html" : "
", 346 | "locals" : { 347 | "var" : "world" 348 | } 349 | }, 350 | 351 | "HTML-style tag multiple CSS classes" : { 352 | "haml" : ".z(class=var)", 353 | "html" : "
", 354 | "locals" : { 355 | "var" : "a" 356 | } 357 | }, 358 | 359 | "HTML-style tag with an atomic attribute" : { 360 | "haml" : "%a(flag)", 361 | "html" : "" 362 | } 363 | }, 364 | 365 | "tags with Ruby-style attributes": { 366 | 367 | "Ruby-style one attribute" : { 368 | "haml" : "%p{:a => 'b'}", 369 | "html" : "

", 370 | "optional" : true 371 | }, 372 | 373 | "Ruby-style attributes hash with whitespace" : { 374 | "haml" : "%p{ :a => 'b' }", 375 | "html" : "

", 376 | "optional" : true 377 | }, 378 | 379 | "Ruby-style interpolated attribute" : { 380 | "haml" : "%p{:a =>\"#{var}\"}", 381 | "html" : "

", 382 | "optional" : true, 383 | "locals" : { 384 | "var" : "value" 385 | } 386 | }, 387 | 388 | "Ruby-style multiple attributes" : { 389 | "haml" : "%p{ :a => 'b', 'c' => 'd' }", 390 | "html" : "

", 391 | "optional" : true 392 | }, 393 | 394 | "Ruby-style attributes separated with newlines" : { 395 | "haml" : "%p{ :a => 'b',\n 'c' => 'd' }", 396 | "html" : "

", 397 | "optional" : true 398 | }, 399 | 400 | "Ruby-style 'class' as an attribute" : { 401 | "haml" : "%p{:class => 'class1'}", 402 | "html" : "

", 403 | "optional" : true 404 | }, 405 | 406 | "Ruby-style tag with a CSS class and 'class' as an attribute" : { 407 | "haml" : "%p.class2{:class => 'class1'}", 408 | "html" : "

", 409 | "optional" : true 410 | }, 411 | 412 | "Ruby-style tag with 'id' as an attribute" : { 413 | "haml" : "%p{:id => '1'}", 414 | "html" : "

", 415 | "optional" : true 416 | }, 417 | 418 | "Ruby-style tag with a CSS id and 'id' as an attribute" : { 419 | "haml" : "%p#id{:id => '1'}", 420 | "html" : "

", 421 | "optional" : true 422 | }, 423 | 424 | "Ruby-style tag with a CSS id and a numeric 'id' as an attribute" : { 425 | "haml" : "%p#id{:id => 1}", 426 | "html" : "

", 427 | "optional" : true 428 | }, 429 | 430 | "Ruby-style tag with a variable attribute" : { 431 | "haml" : "%p{:class => var}", 432 | "html" : "

", 433 | "optional" : true, 434 | "locals" : { 435 | "var" : "hello" 436 | } 437 | }, 438 | 439 | "Ruby-style tag with a CSS class and 'class' as a variable attribute" : { 440 | "haml" : ".hello{:class => var}", 441 | "html" : "
", 442 | "optional" : true, 443 | "locals" : { 444 | "var" : "world" 445 | } 446 | }, 447 | 448 | "Ruby-style tag multiple CSS classes" : { 449 | "haml" : ".z{:class => var}", 450 | "html" : "
", 451 | "optional" : true, 452 | "locals" : { 453 | "var" : "a" 454 | } 455 | } 456 | }, 457 | 458 | "tags with multiple types of classes": { 459 | "HTML-style class and Ruby-style class": { 460 | "haml": "%div{class: 'hash'}(class='html')", 461 | "html": "
", 462 | "optional": true 463 | }, 464 | "Three types of multiple classes come out in correct order": { 465 | "haml": ".foo.moo{:class => ['bar', 'alpha']}(class='baz')", 466 | "html": "
", 467 | "optional": true 468 | } 469 | }, 470 | 471 | "silent comments" : { 472 | 473 | "an inline silent comment" : { 474 | "haml" : "-# hello", 475 | "html" : "" 476 | }, 477 | 478 | "a nested silent comment" : { 479 | "haml" : "-#\n hello", 480 | "html" : "" 481 | }, 482 | 483 | "a multiply nested silent comment" : { 484 | "haml" : "-#\n %div\n foo", 485 | "html" : "" 486 | }, 487 | 488 | "a multiply nested silent comment with inconsistent indents" : { 489 | "haml" : "-#\n %div\n foo", 490 | "html" : "" 491 | } 492 | }, 493 | 494 | "markup comments" : { 495 | 496 | "an inline markup comment" : { 497 | "haml" : "/ comment", 498 | "html" : "" 499 | }, 500 | 501 | "a nested markup comment" : { 502 | "haml" : "/\n comment\n comment2", 503 | "html" : "" 504 | } 505 | }, 506 | 507 | "conditional comments": { 508 | "a conditional comment" : { 509 | "haml" : "/[if IE]\n %p a", 510 | "html" : "" 511 | } 512 | }, 513 | 514 | "internal filters": { 515 | 516 | "content in an 'escaped' filter" : { 517 | "haml" : ":escaped\n <&\">", 518 | "html" : "<&">" 519 | }, 520 | 521 | "content in a 'preserve' filter" : { 522 | "haml" : ":preserve\n hello\n\n%p", 523 | "html" : "hello \n

" 524 | }, 525 | 526 | "content in a 'plain' filter" : { 527 | "haml" : ":plain\n hello\n\n%p", 528 | "html" : "hello\n

" 529 | }, 530 | 531 | "content in a 'css' filter (XHTML)" : { 532 | "haml" : ":css\n hello\n\n%p", 533 | "html" : "\n

", 534 | "config" : { 535 | "format" : "xhtml" 536 | } 537 | }, 538 | 539 | "content in a 'javascript' filter (XHTML)" : { 540 | "haml" : ":javascript\n a();\n%p", 541 | "html" : "\n

", 542 | "config" : { 543 | "format" : "xhtml" 544 | } 545 | }, 546 | 547 | "content in a 'css' filter (HTML)" : { 548 | "haml" : ":css\n hello\n\n%p", 549 | "html" : "\n

", 550 | "config" : { 551 | "format" : "html5" 552 | } 553 | }, 554 | 555 | "content in a 'javascript' filter (HTML)" : { 556 | "haml" : ":javascript\n a();\n%p", 557 | "html" : "\n

", 558 | "config" : { 559 | "format" : "html5" 560 | } 561 | } 562 | }, 563 | 564 | "Ruby-style interpolation": { 565 | 566 | "interpolation inside inline content" : { 567 | "haml" : "%p #{var}", 568 | "html" : "

value

", 569 | "optional" : true, 570 | "locals" : { 571 | "var" : "value" 572 | } 573 | }, 574 | 575 | "no interpolation when escaped" : { 576 | "haml" : "%p \\#{var}", 577 | "html" : "

#{var}

", 578 | "optional" : true, 579 | "locals" : { 580 | "var" : "value" 581 | } 582 | }, 583 | 584 | "interpolation when the escape character is escaped" : { 585 | "haml" : "%p \\\\#{var}", 586 | "html" : "

\\value

", 587 | "optional" : true, 588 | "locals" : { 589 | "var" : "value" 590 | } 591 | }, 592 | 593 | "interpolation inside filtered content" : { 594 | "haml" : ":plain\n #{var} interpolated: #{var}", 595 | "html" : "value interpolated: value", 596 | "optional" : true, 597 | "locals" : { 598 | "var" : "value" 599 | } 600 | } 601 | }, 602 | 603 | "HTML escaping" : { 604 | 605 | "code following '&='" : { 606 | "haml" : "&= '<\"&>'", 607 | "html" : "<"&>" 608 | }, 609 | 610 | "code following '=' when escape_haml is set to true" : { 611 | "haml" : "= '<\"&>'", 612 | "html" : "<"&>", 613 | "config" : { 614 | "escape_html" : "true" 615 | } 616 | }, 617 | 618 | "code following '!=' when escape_haml is set to true" : { 619 | "haml" : "!= '<\"&>'", 620 | "html" : "<\"&>", 621 | "config" : { 622 | "escape_html" : "true" 623 | } 624 | } 625 | 626 | }, 627 | 628 | "boolean attributes" : { 629 | 630 | "boolean attribute with XHTML" : { 631 | "haml" : "%input(checked=true)", 632 | "html" : "", 633 | "config" : { 634 | "format" : "xhtml" 635 | } 636 | }, 637 | 638 | "boolean attribute with HTML" : { 639 | "haml" : "%input(checked=true)", 640 | "html" : "", 641 | "config" : { 642 | "format" : "html5" 643 | } 644 | } 645 | }, 646 | 647 | "whitespace preservation" : { 648 | 649 | "following the '~' operator" : { 650 | "haml" : "~ \"Foo\\n
Bar\\nBaz
\"", 651 | "html" : "Foo\n
Bar
Baz
", 652 | "optional" : true 653 | }, 654 | 655 | "inside a textarea tag" : { 656 | "haml" : "%textarea\n hello\n hello", 657 | "html" : "" 658 | }, 659 | 660 | "inside a pre tag" : { 661 | "haml" : "%pre\n hello\n hello", 662 | "html" : "
hello\nhello
" 663 | } 664 | }, 665 | 666 | "whitespace removal" : { 667 | 668 | "a tag with '>' appended and inline content" : { 669 | "haml" : "%li hello\n%li> world\n%li again", 670 | "html" : "
  • hello
  • world
  • again
  • " 671 | }, 672 | 673 | "a tag with '>' appended and nested content" : { 674 | "haml" : "%li hello\n%li>\n world\n%li again", 675 | "html" : "
  • hello
  • \nworld\n
  • again
  • " 676 | }, 677 | 678 | "a tag with '<' appended" : { 679 | "haml" : "%p<\n hello\n world", 680 | "html" : "

    hello\nworld

    " 681 | } 682 | } 683 | } 684 | --------------------------------------------------------------------------------