Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,185 @@
exports.test_data = {
default_options: [
{ name: "indent_size", value: "1" },
{ name: "indent_char", value: "'\\t'" },
{ name: "selector_separator_newline", value: "true" },
{ name: "end_with_newline", value: "false" },
{ name: "newline_between_rules", value: "false"},
],
groups: [{
name: "End With Newline",
description: "",
matrix: [
{
options: [
{ name: "end_with_newline", value: "true" }
],
eof: '\\n'
}, {
options: [
{ name: "end_with_newline", value: "false" }
],
eof: ''
}
],
tests: [
{ fragment: true, input: '', output: '{{eof}}' },
{ fragment: true, input: ' .tabs{}', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: ' \n\n.tabs{}\n\n\n\n', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
}, {
name: "Empty braces",
description: "",
tests: [
{ input: '.tabs{}', output: '.tabs {}' },
{ input: '.tabs { }', output: '.tabs {}' },
{ input: '.tabs { }', output: '.tabs {}' },
// When we support preserving newlines this will need to change
{ input: '.tabs \n{\n \n }', output: '.tabs {}' }
],
}, {
name: "",
description: "",
tests: [
{
input: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}',
output: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}'
},
],
}, {
name: 'Selector Separator',
description: '',
matrix: [
{
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
],
separator: ' ',
separator1: ' '
}, {
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
],
// BUG: #713
separator: ' ',
separator1: ' '
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
],
separator: '\\n',
separator1: '\\n\\t'
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
],
separator: '\\n',
separator1: '\\n\\t'
}
],
tests: [
{ input: '#bla, #foo{color:green}', output: '#bla,{{separator}}#foo {\n\tcolor: green\n}'},
{ input: '@media print {.tab{}}', output: '@media print {\n\t.tab {}\n}'},
{ input: '@media print {.tab,.bat{}}', output: '@media print {\n\t.tab,{{separator1}}.bat {}\n}'},
{ input: '#bla, #foo{color:black}', output: '#bla,{{separator}}#foo {\n\tcolor: black\n}'},
{ input: 'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}',
output: 'a:first-child,{{separator}}a:first-child {\n\tcolor: red;\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}'}
]
}, {
name: "Newline Between Rules",
description: "",
matrix: [
{
options: [
{ name: "newline_between_rules", value: "true" }
],
separator: '\\n'
}, {
options: [
{ name: "newline_between_rules", value: "false" }
],
separator: ''
}
],
tests: [
{ input: '.div {}\n.span {}', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div{}\n \n.span{}', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div {} \n \n.span { } \n', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div {\n \n} \n .span {\n } ', output: '.div {}\n{{separator}}.span {}'},
{ input: '.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', output: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n{{separator}}@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}'},
{ input: 'a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
],
}, {
name: "Functions braces",
description: "",
tests: [
{ input: '.tabs(){}', output: '.tabs() {}' },
{ input: '.tabs (){}', output: '.tabs () {}' },
{ input: '.tabs (pa, pa(1,2)), .cols { }', output: '.tabs (pa, pa(1, 2)),\n.cols {}' },
{ input: '.tabs(pa, pa(1,2)), .cols { }', output: '.tabs(pa, pa(1, 2)),\n.cols {}' },
{ input: '.tabs ( ) { }', output: '.tabs () {}' },
{ input: '.tabs( ) { }', output: '.tabs() {}' },
{ input: '.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', output: '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}' },
{ input: '.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}' }
],
}, {
name: "Comments",
description: "",
tests: [
{ unchanged: '/* test */' },
{ input: '.tabs{/* test */}', output: '.tabs {\n\t/* test */\n}' },
{ input: '.tabs{/* test */}', output: '.tabs {\n\t/* test */\n}' },
{ input: '/* header */.tabs {}', output: '/* header */\n\n.tabs {}' },
{ input: '.tabs {\n/* non-header */\nwidth:10px;}', output: '.tabs {\n\t/* non-header */\n\twidth: 10px;\n}' },
{ unchanged: '/* header' },
{ unchanged: '// comment' },
{ input: '.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}',
output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}'},
{ comment: "single line comment support (less/sass)",
input:'.tabs{\n// comment\nwidth:10px;\n}', output: '.tabs {\n\t// comment\n\twidth: 10px;\n}' },
{ input: '.tabs{// comment\nwidth:10px;\n}', output: '.tabs {\n\t// comment\n\twidth: 10px;\n}' },
{ input: '//comment\n.tabs{width:10px;}', output: '//comment\n.tabs {\n\twidth: 10px;\n}' },
{ input: '.tabs{//comment\n//2nd single line comment\nwidth:10px;}', output: '.tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}' },
{ input: '.tabs{width:10px;//end of line comment\n}', output: '.tabs {\n\twidth: 10px; //end of line comment\n}' },
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px;\n}' },
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}' }
],
}, {
name: "Psuedo-classes vs Variables",
description: "",
tests: [
{ unchanged: '@page :first {}' },
{ comment: "Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.",
input: '@page:first {}',
output: '@page: first {}' },
{ unchanged: '@page: first {}' }
],
}, {
name: "SASS/SCSS",
description: "",
tests: [
{ comment: "Basic Interpolation",
unchanged: 'p {\n\t$font-size: 12px;\n\t$line-height: 30px;\n\tfont: #{$font-size}/#{$line-height};\n}'},
{ unchanged: 'p.#{$name} {}' },
{ unchanged: [
'@mixin itemPropertiesCoverItem($items, $margin) {',
'\twidth: calc((100% - ((#{$items} - 1) * #{$margin}rem)) / #{$items});',
'\tmargin: 1.6rem #{$margin}rem 1.6rem 0;',
'}'
]}
],
}, {
}]
}

View file

@ -0,0 +1,473 @@
exports.test_data = {
default_options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "preserve_newlines", value: "true" },
{ name: "jslint_happy", value: "false" },
{ name: "keep_array_indentation", value: "false" },
{ name: "brace_style", value: "'collapse'" },
{ name: "extra_liners", value: "['html', 'head', '/html']" }
],
groups: [{
name: "End With Newline",
description: "",
matrix: [
{
options: [
{ name: "end_with_newline", value: "true" }
],
eof: '\\n'
}, {
options: [
{ name: "end_with_newline", value: "false" }
],
eof: ''
}
],
tests: [
{ fragment: true, input: '', output: '{{eof}}' },
{ fragment: true, input: '<div></div>', output: '<div></div>{{eof}}' },
// { fragment: true, input: ' \n\n<div></div>\n\n\n\n', output: ' <div></div>{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
}, {
name: "Custom Extra Liners (empty)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "[]" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n <p>x</p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Custom Extra Liners (default)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "null" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head></head><body></body></html>',
output: '<html>\n\n<head></head>\n\n<body></body>\n\n</html>'
}
],
}, {
name: "Custom Extra Liners (p, string)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "'p,/p'" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Custom Extra Liners (p)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "['p', '/p']" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Attribute Wrap",
description: "Wraps attributes inside of html tags",
matrix: [
{
options: [
{ name: "wrap_attributes", value: "'force'" }
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'force'" },
{ name: "wrap_line_length", value: "80" }
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'force'" },
{ name: "wrap_attributes_indent_size", value: "8" },
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'auto'" },
{ name: "wrap_line_length", value: "80" }
],
eof: ' ',
indent_attr: '',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'auto'" },
{ name: "wrap_line_length", value: "0" }
],
eof: ' ',
indent_attr: '',
over80: ' '
}
],
tests: [
{
fragment: true,
input: '<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>',
output: '<div attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here">This is some text</div>'
},
{
fragment: true,
input: '<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>',
output: '<div lookatthissuperduperlongattributenamewhoahcrazy0="true"{{eof}}{{indent_attr}}attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here"{{over80}}{{indent_attr}}heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>'
},
{
fragment: true,
input: '<img attr0 attr1="123" data-attr2="hello t here"/>',
output: '<img attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here" />'
}
]
}, {
name: "Handlebars Indenting Off",
description: "Test handlebar behavior when indenting is off",
template: "^^^ $$$",
options: [
{ name: "indent_handlebars", value: "false" }
],
tests: [
{ fragment: true,
input_:
'{{#if 0}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}',
output:
'{{#if 0}}\n' +
'<div>\n' +
'</div>\n' +
'{{/if}}' },
{ fragment: true,
input_:
'<div>\n' +
'{{#each thing}}\n' +
' {{name}}\n' +
'{{/each}}\n' +
'</div>',
output:
'<div>\n' +
' {{#each thing}} {{name}} {{/each}}\n' +
'</div>'}
]
}, {
name: "Handlebars Indenting On",
description: "Test handlebar formatting",
template: "^^^ $$$",
matrix: [
{
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{field}}'
}, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{! comment}}'
}, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{pre{{field1}} {{field2}} {{field3}}post'
}
, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{! \\n mult-line\\ncomment \\n with spacing\\n}}'
}
],
tests: [
{ fragment: true, unchanged: '{{page-title}}' },
{ fragment: true, unchanged: '{{#if 0}}{{/if}}' },
{ fragment: true, unchanged: '{{#if 0}}^^^content$$${{/if}}' },
{ fragment: true, unchanged: '{{#if 0}}\n{{/if}}' },
{ fragment: true,
input_: '{{#if words}}{{/if}}',
output: '{{#if words}}{{/if}}' },
{ fragment: true,
input_: '{{#if words}}^^^content$$${{/if}}',
output: '{{#if words}}^^^content$$${{/if}}' },
{ fragment: true,
input_: '{{#if words}}^^^content$$${{/if}}',
output: '{{#if words}}^^^content$$${{/if}}' },
{ fragment: true,
unchanged:
'{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if 1}}\n' +
'<div>\n' +
'</div>\n' +
'{{/if}}',
output:
'{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
{ fragment: true,
unchanged:
'<div>\n' +
' {{#if 1}}\n' +
' {{/if}}\n' +
'</div>' },
{ fragment: true,
input_:
'<div>\n' +
'{{#if 1}}\n' +
'{{/if}}\n' +
'</div>',
output:
'<div>\n' +
' {{#if 1}}\n' +
' {{/if}}\n' +
'</div>' },
{ fragment: true,
input_:
'{{#if}}\n' +
'{{#each}}\n' +
'{{#if}}\n' +
'^^^content$$$\n' +
'{{/if}}\n' +
'{{#if}}\n' +
'^^^content$$$\n' +
'{{/if}}\n' +
'{{/each}}\n' +
'{{/if}}',
output:
'{{#if}}\n' +
' {{#each}}\n' +
' {{#if}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
' {{#if}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
' {{/each}}\n' +
'{{/if}}' },
{ fragment: true, unchanged: '{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
// Test {{else}} aligned with {{#if}} and {{/if}}
{ fragment: true,
input_:
'{{#if 1}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
' ^^^content$$$\n' +
'{{/if}}',
output:
'{{#if 1}}\n' +
' ^^^content$$$\n' +
'{{else}}\n' +
' ^^^content$$$\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if 1}}\n' +
' {{else}}\n' +
' {{/if}}',
output:
'{{#if 1}}\n' +
'{{else}}\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if thing}}\n' +
'{{#if otherthing}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
'^^^content$$$\n' +
' {{/if}}\n' +
' {{else}}\n'+
'^^^content$$$\n' +
'{{/if}}',
output:
'{{#if thing}}\n' +
' {{#if otherthing}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
'{{else}}\n'+
' ^^^content$$$\n' +
'{{/if}}' },
// Test {{}} inside of <> tags, which should be separated by spaces
// for readability, unless they are inside a string.
{ fragment: true,
input_: '<div{{somestyle}}></div>',
output: '<div {{somestyle}}></div>' },
{ fragment: true,
input_: '<div{{#if test}}class="foo"{{/if}}>^^^content$$$</div>',
output: '<div {{#if test}} class="foo" {{/if}}>^^^content$$$</div>' },
{ fragment: true,
input_: '<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>^^^content$$$</div>',
output: '<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>^^^content$$$</div>' },
{ fragment: true,
input_: '<span{{#if condition}}class="foo"{{/if}}>^^^content$$$</span>',
output: '<span {{#if condition}} class="foo" {{/if}}>^^^content$$$</span>' },
{ fragment: true,
unchanged: '<div unformatted="{{#if}}^^^content$$${{/if}}">^^^content$$$</div>' },
{ fragment: true,
unchanged: '<div unformatted="{{#if }} ^^^content$$${{/if}}">^^^content$$$</div>' },
// Quotes found inside of Handlebars expressions inside of quoted
// strings themselves should not be considered string delimiters.
{ fragment: true,
unchanged: '<div class="{{#if thingIs "value"}}^^^content$$${{/if}}"></div>' },
{ fragment: true,
unchanged: '<div class="{{#if thingIs \\\'value\\\'}}^^^content$$${{/if}}"></div>' },
{ fragment: true,
unchanged: '<div class=\\\'{{#if thingIs "value"}}^^^content$$${{/if}}\\\'></div>' },
{ fragment: true,
unchanged: '<div class=\\\'{{#if thingIs \\\'value\\\'}}^^^content$$${{/if}}\\\'></div>' }
],
}, {
name: "Unclosed html elements",
description: "Unclosed elements should not indent",
options: [],
tests: [
{ fragment: true, unchanged: '<source>\n<source>' },
{ fragment: true, unchanged: '<br>\n<br>' },
{ fragment: true, unchanged: '<input>\n<input>' },
{ fragment: true, unchanged: '<meta>\n<meta>' },
{ fragment: true, unchanged: '<link>\n<link>' }
]
}, {
name: "Unformatted tags",
description: "Unformatted tag behavior",
options: [],
tests: [
{ fragment: true, unchanged: '<ol>\n <li>b<pre>c</pre></li>\n</ol>' },
{ fragment: true, unchanged: '<ol>\n <li>b<code>c</code></li>\n</ol>' },
]
}, {
name: "Php formatting",
description: "Php (<?php ... ?>) treated as comments.",
options: [],
tests: [
{ fragment: true, unchanged: '<h1 class="content-page-header"><?=$view["name"]; ?></h1>' },
{ fragment: true, unchanged:
[
'<?php',
'for($i = 1; $i <= 100; $i++;) {',
' #count to 100!',
' echo($i . "</br>");',
'}',
'?>'
]
},
]
}, {
name: "underscore.js formatting",
description: "underscore.js templates (<% ... %>) treated as comments.",
options: [],
tests: [
{ fragment: true, unchanged:
[
'<div class="col-sm-9">',
' <textarea id="notes" class="form-control" rows="3">',
' <%= notes %>',
' </textarea>',
'</div>'
]
},
]
}, {
name: "Indent with tabs",
description: "Use one tab instead of several spaces for indentation",
template: "^^^ $$$",
options: [
{ name: "indent_with_tabs", value: "true" }
],
tests: [
{ fragment: true,
input_:
'<div>\n' +
'<div>\n' +
'</div>\n' +
'</div>',
output:
'<div>\n' +
'\t<div>\n' +
'\t</div>\n' +
'</div>' }
]
}, {
name: "Indent without tabs",
description: "Use several spaces for indentation",
template: "^^^ $$$",
options: [
{ name: "indent_with_tabs", value: "false" }
],
tests: [
{ fragment: true,
input_:
'<div>\n' +
'<div>\n' +
'</div>\n' +
'</div>',
output:
'<div>\n' +
' <div>\n' +
' </div>\n' +
'</div>' }
]
}, {
name: "New Test Suite"
}],
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
#!/usr/bin/env node
var fs = require('fs');
var mustache = require('mustache');
function generate_tests() {
var test_data, template;
// javascript
test_data = require(__dirname + '/data/javascript.js').test_data;
set_formatters(test_data, 'bt', '// ')
template = fs.readFileSync(__dirname + '/template/node-javascript.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-javascript-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
set_formatters(test_data, 'bt', '# ')
template = fs.readFileSync(__dirname + '/template/python-javascript.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../python/jsbeautifier/tests/testjsbeautifier.py', mustache.render(template, test_data), {encoding: 'utf-8'});
// css
test_data = require(__dirname + '/data/css.js').test_data;
set_formatters(test_data, 't', '// ')
template = fs.readFileSync(__dirname + '/template/node-css.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-css-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
set_formatters(test_data, 't', '# ')
template = fs.readFileSync(__dirname + '/template/python-css.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../python/cssbeautifier/tests/test.py', mustache.render(template, test_data), {encoding: 'utf-8'});
// html
test_data = require(__dirname + '/data/html.js').test_data;
set_formatters(test_data, 'bth', '// ')
template = fs.readFileSync(__dirname + '/template/node-html.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-html-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
// no python html beautifier, so no tests
}
function isStringOrArray(val) {
return typeof val === 'string' || val instanceof Array;
}
function getTestString(val) {
if (typeof val === 'string') {
return "'" + val.replace(/\n/g,'\\n').replace(/\t/g,'\\t') + "'";
} else if (val instanceof Array) {
return "'" + val.join("\\n' +\n '").replace(/\t/g,'\\t') + "'";
} else {
return null;
}
}
function set_formatters (data, test_method, comment_mark) {
// utility mustache functions
data.matrix_context_string = function() {
var context = this;
return function(text, render) {
var outputs = [];
// text is ignored for this
for (var name in context) {
if (name === 'options') {
continue;
}
if (context.hasOwnProperty(name)) {
outputs.push(name + ' = "' + context[name] + '"');
}
}
return render(outputs.join(', '));
}
};
data.test_line = function() {
return function(text, render) {
var method_text = test_method;
if (this.fragment) {
method_text = 'test_fragment';
}
// text is ignored for this.
var comment = '';
if (typeof this.comment === 'string') {
comment = '\n ' + comment_mark + this.comment + '\n ';
} else if (this.comment instanceof Array) {
comment = '\n ' + comment_mark + this.comment.join('\n ' + comment_mark) + '\n ';
}
var input = null;
var before_input = method_text + '(';
var before_output = ', ';
function set_input(field, opt_newline) {
if (input !== null && isStringOrArray(field)) {
throw "Only one test input field allowed (input, input_, or unchanged): " + input;
}
if (typeof field === 'string' && !opt_newline) {
input = getTestString(field);
} else if (field instanceof Array || (typeof field === 'string' && opt_newline)) {
before_input = method_text + '(\n ';
before_output = ',\n ';
input = getTestString(field);
}
}
set_input(this.input);
// allow underscore for formatting alignment with "output"
set_input(this.input_, true);
// use "unchanged" instead of "input" if there is no output
set_input(this.unchanged);
if(isStringOrArray(this.unchanged) && isStringOrArray(this.output)) {
throw "Cannot specify 'output' with 'unchanged' test input: " + input;
}
if (input === null) {
throw "Missing test input field (input, input_, or unchanged).";
}
var output = null;
if (typeof this.output === 'string') {
output = getTestString(this.output);
} else if (this.output instanceof Array) {
before_input = method_text + '(\n ';
before_output = ',\n ';
output = getTestString(this.output);
} else {
before_output = '';
}
if (input === output) {
throw "Test strings are identical. Omit 'output' and use 'unchanged': " + input;
}
if(output && output.indexOf('<%') !== -1) {
mustache.tags = ['<%', '%>'];
}
input = render(input);
output = render(output);
if(output && output.indexOf('<%') !== -1) {
mustache.tags = ['{{', '}}'];
}
if (output === input) {
before_output = '';
output = '';
}
return comment + before_input + input + before_output + output + ')';
}
};
data.set_mustache_tags = function() {
return function(text, render) {
if(this.template) {
mustache.tags = this.template.split(' ');
}
return '';
}
};
data.unset_mustache_tags = function() {
return function(text, render) {
if(this.template) {
mustache.tags = ['{{', '}}'];
}
return '';
}
};
}
generate_tests();

View file

@ -0,0 +1,15 @@
echo "Post-build git status check..."
echo "Ensuring no changes visible to git have been made to '$*' ..."
git status $* | grep "nothing to commit.*working directory clean" || {
# we should find nothing to commit. If we don't, build has failed.
echo "ERROR: Post-build git status check - FAILED."
echo "Git status reported changes to non-git-ignore'd files."
echo "TO REPRO: Run 'git status $*'. The use git gui or git diff to see what was changed during the build."
echo "TO FIX: Amend your commit and rebuild. Repeat until git status reports no changes both before and after the build."
echo "OUTPUT FOR 'git status $*':"
git status $*
exit 1
}
echo "Post-build git status check - Succeeded."
exit 0

View file

@ -0,0 +1,218 @@
/*global js_beautify: true */
function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false,
newline_between_rules: true
};
function test_css_beautifier(input)
{
return css_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test css
function t(input, expectation)
{
var wrapped_input, wrapped_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_css_beautifier, 'css_beautify');
test_fragment(input, expectation);
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
t(".tabs {}");
{{#default_options}} opts.{{name}} = {{&value}};
{{/default_options}}
{{#groups}}
{{^matrix}}
// {{&name}}
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}
{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}
{{/groups}}
// test basic css beautifier
t(".tabs {}");
t(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}");
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}");
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}");
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}");
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
//lead-in whitespace determines base-indent.
// lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }");
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }");
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// separate selectors
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// block nesting
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
/*
@font-face {
font-family: 'Bitstream Vera Serif Bold';
src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
}
@media screen {
#foo:hover {
background-image: url(foo.png);
}
@media screen and (min-device-pixel-ratio: 2) {
@font-face {
font-family: 'Helvetica Neue'
}
#foo:hover {
background-image: url(foo@2x.png);
}
}
}
*/
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}");
// less-css cases
t('.well{@well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}');
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}');
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// Not sure if this is sensible
// but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// import
t('@import "test";');
// don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
// handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
//nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}");
//nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}");
//variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
// test options
opts.indent_size = 2;
opts.indent_char = ' ';
opts.selector_separator_newline = false;
// pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}");
t("#foo *:hover {\n color: purple\n}");
t("::selection {\n color: #ff0000;\n}");
// TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}");
// particular edge case with braces and semicolons inside tags that allows custom text
t("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}");
// may not eat the space before "["
t('html.js [data-custom="123"] {\n opacity: 1.00;\n}');
t('html.js *[data-custom="123"] {\n opacity: 1.00;\n}');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_css_tests = run_css_tests;
}

View file

@ -0,0 +1,393 @@
/*global js_beautify: true */
function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false
};
function test_html_beautifier(input)
{
return html_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test html
function bth(input, expectation)
{
var wrapped_input, wrapped_expectation, field_input, field_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_html_beautifier, 'html_beautify');
test_fragment(input, expectation);
if (opts.indent_size === 4 && input) {
wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
test_fragment(wrapped_input, wrapped_expectation);
}
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
bth('');
{{#default_options}} opts.{{name}} = {{&value}};
{{/default_options}}
{{#groups}}{{#set_mustache_tags}}.{{/set_mustache_tags}}{{^matrix}}
// {{&name}}
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}{{#unset_mustache_tags}}.{{/unset_mustache_tags}}
{{/groups}}
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>\n');
test_fragment('<div></div>\n\n\n', '<div></div>\n');
test_fragment('<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
'\n' +
' </script>\n' +
'</head>\n');
opts.end_with_newline = false;
// error cases need love too
bth('<img title="Bad food!" src="foo.jpg" alt="Evil" ">');
bth("<!-- don't blow up if a comment is not complete"); // -->
test_fragment(
'<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
' </script>\n' +
'</head>');
test_fragment('<div></div>\n', '<div></div>');
bth('<div></div>');
bth('<div>content</div>');
bth('<div><div></div></div>',
'<div>\n' +
' <div></div>\n' +
'</div>');
bth('<div><div>content</div></div>',
'<div>\n' +
' <div>content</div>\n' +
'</div>');
bth('<div>\n' +
' <span>content</span>\n' +
'</div>');
bth('<div>\n' +
'</div>');
bth('<div>\n' +
' content\n' +
'</div>');
bth('<div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth(' <div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth('<div>\n' +
'</div>\n' +
' <div>\n' +
' </div>',
'<div>\n' +
'</div>\n' +
'<div>\n' +
'</div>');
bth(' <div>\n' +
'</div>',
'<div>\n' +
'</div>');
bth('<div >content</div>',
'<div>content</div>');
bth('<div thinger="preserve space here" ></div >',
'<div thinger="preserve space here"></div>');
bth('content\n' +
' <div>\n' +
' </div>\n' +
'content',
'content\n' +
'<div>\n' +
'</div>\n' +
'content');
bth('<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
'<div>\n' +
'</div>\n' +
'</li>',
'<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
' content\n' +
'</li>\n' +
'<li>\n' +
' content\n' +
'</li>');
bth('<img>content');
bth('<img> content');
bth('<img> content', '<img> content');
bth('<img><img>content');
bth('<img> <img>content');
bth('<img> <img>content', '<img> <img>content');
bth('<img><b>content</b>');
bth('<img> <b>content</b>');
bth('<img> <b>content</b>', '<img> <b>content</b>');
bth('<div>content<img>content</div>');
bth('<div> content <img> content</div>');
bth('<div> content <img> content </div>',
'<div> content <img> content </div>');
bth('Text <a href="#">Link</a> Text');
// START tests for issue 453
bth('<script type="text/unknown"><div></div></script>',
'<script type="text/unknown">\n' +
' <div></div>\n' +
'</script>');
bth('<script type="text/javascript"><div></div></script>',
'<script type="text/javascript">\n' +
' < div > < /div>\n' +
'</script>');
bth('<script><div></div></script>',
'<script>\n' +
' < div > < /div>\n' +
'</script>');
bth('<script type="text/javascript">var foo = "bar";</script>',
'<script type="text/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript">var foo = "bar";</script>',
'<script type="application/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript;version=1.8">var foo = "bar";</script>',
'<script type="application/javascript;version=1.8">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/x-javascript">var foo = "bar";</script>',
'<script type="application/x-javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/ecmascript">var foo = "bar";</script>',
'<script type="application/ecmascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="text/javascript1.5">var foo = "bar";</script>',
'<script type="text/javascript1.5">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script>var foo = "bar";</script>',
'<script>\n' +
' var foo = "bar";\n' +
'</script>');
bth('<style type="text/unknown"><tag></tag></style>',
'<style type="text/unknown">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css"><tag></tag></style>',
'<style type="text/css">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style><tag></tag></style>',
'<style>\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css">.selector {font-size:12px;}</style>',
'<style type="text/css">\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
bth('<style>.selector {font-size:12px;}</style>',
'<style>\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
// END tests for issue 453
var unformatted = opts.unformatted;
opts.unformatted = ['script', 'style'];
bth('<script id="javascriptTemplate" type="text/x-kendo-template">\n' +
' <ul>\n' +
' # for (var i = 0; i < data.length; i++) { #\n' +
' <li>#= data[i] #</li>\n' +
' # } #\n' +
' </ul>\n' +
'</script>');
bth('<style>\n' +
' body {background-color:lightgrey}\n' +
' h1 {color:blue}\n' +
'</style>');
opts.unformatted = unformatted;
unformatted = opts.unformatted;
opts.unformatted = ['custom-element'];
test_fragment('<div>should <custom-element>not</custom-element>' +
' insert newlines</div>',
'<div>should <custom-element>not</custom-element>' +
' insert newlines</div>');
opts.unformatted = unformatted;
// Tests that don't pass, but probably should.
// bth('<div><span>content</span></div>');
// Handlebars tests
// Without the indent option on, handlebars are treated as content.
opts.wrap_line_length = 0;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of 0 means no max line length, and should not wrap.
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
opts.wrap_line_length = "0";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of "0" means no max line length, and should not wrap
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
//BUGBUG: This should wrap before 40 not after.
opts.wrap_line_length = 40;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.wrap_line_length = "40";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.indent_size = 1;
opts.indent_char = '\t';
opts.preserve_newlines = false;
bth('<div>\n\tfoo\n</div>', '<div> foo </div>');
opts.preserve_newlines = true;
bth('<div>\n\tfoo\n</div>');
// test preserve_newlines and max_preserve_newlines
opts.preserve_newlines = false;
bth('<div>Should not</div>\n\n\n' +
'<div>preserve newlines</div>',
'<div>Should not</div>\n' +
'<div>preserve newlines</div>');
opts.preserve_newlines = true;
opts.max_preserve_newlines = 0;
bth('<div>Should</div>\n\n\n' +
'<div>preserve zero newlines</div>',
'<div>Should</div>\n' +
'<div>preserve zero newlines</div>');
opts.max_preserve_newlines = 1;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n' +
'<div>preserve one newline</div>');
opts.max_preserve_newlines = null;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_html_tests = run_html_tests;
}

View file

@ -0,0 +1,225 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import cssbeautifier
class CSSBeautifierTest(unittest.TestCase):
def resetOptions(self):
false = False
true = True
self.options = cssbeautifier.default_options()
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
def testGenerated(self):
self.resetOptions()
test_fragment = self.decodesto
t = self.decodesto
false = False
true = True
{{#default_options}} self.options.{{name}} = {{&value}}
{{/default_options}}
{{#groups}}
{{^matrix}}
# {{&name}}
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}}
{{/tests}}
{{/matrix}}
{{#matrix}}
# {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}}
{{/tests}}
{{/matrix}}
{{/groups}}
def testNewline(self):
self.resetOptions()
t = self.decodesto
self.options.end_with_newline = True
t("", "\n")
t("\n", "\n")
t(".tabs{}\n", ".tabs {}\n")
t(".tabs{}", ".tabs {}\n")
def testBasics(self):
self.resetOptions()
t = self.decodesto
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
t(".tabs{}", ".tabs {}")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}")
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
# lead-in whitespace determines base-indent.
# lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testSeperateSelectors(self):
self.resetOptions()
t = self.decodesto
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testBlockNesting(self):
self.resetOptions()
t = self.decodesto
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
# @font-face {
# font-family: 'Bitstream Vera Serif Bold';
# src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
# }
# @media screen {
# #foo:hover {
# background-image: url(foo.png);
# }
# @media screen and (min-device-pixel-ratio: 2) {
# @font-face {
# font-family: 'Helvetica Neue'
# }
# #foo:hover {
# background-image: url(foo@2x.png);
# }
# }
# }
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}")
def testOptions(self):
self.resetOptions()
self.options.indent_size = 2
self.options.indent_char = ' '
self.options.selector_separator_newline = False
t = self.decodesto
# pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}")
t("#foo *:hover {\n color: purple\n}")
t("::selection {\n color: #ff0000;\n}")
# TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}")
# particular edge case with braces and semicolons inside tags that allows custom text
t( "a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}")
def testLessCss(self):
self.resetOptions()
t = self.decodesto
t('.well{ \n @well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}')
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}')
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}')
# Not sure if this is sensible
# but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
# import
t('@import "test";');
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}")
# nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}")
# variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
def decodesto(self, input, expectation=None):
if expectation == None:
expectation = input
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
if not expectation != input:
self.assertMultiLineEqual(
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
input = input.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
if __name__ == '__main__':
unittest.main()

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff