mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
- moved tojs.js into spacebars Some failing tests in `spacebars-tests` and `ui` ``` meteor test-packages spacebars htmljs html spacebars-tests ui ```
317 lines
10 KiB
JavaScript
317 lines
10 KiB
JavaScript
var Scanner = HTML._$.Scanner;
|
|
var getContent = HTML._$.getContent;
|
|
|
|
var CharRef = HTML.CharRef;
|
|
var Comment = HTML.Comment;
|
|
var Special = HTML.Special;
|
|
|
|
var BR = HTML.BR;
|
|
var HR = HTML.HR;
|
|
var INPUT = HTML.INPUT;
|
|
var A = HTML.A;
|
|
var DIV = HTML.DIV;
|
|
var P = HTML.P;
|
|
var TEXTAREA = HTML.TEXTAREA;
|
|
|
|
Tinytest.add("html - parser getContent", function (test) {
|
|
|
|
var succeed = function (input, expected) {
|
|
var endPos = input.indexOf('^^^');
|
|
if (endPos < 0)
|
|
endPos = input.length;
|
|
|
|
var scanner = new Scanner(input.replace('^^^', ''));
|
|
var result = getContent(scanner);
|
|
test.equal(scanner.pos, endPos);
|
|
test.equal(HTML.toJS(result), HTML.toJS(expected));
|
|
};
|
|
|
|
var fatal = function (input, messageContains) {
|
|
var scanner = new Scanner(input);
|
|
var error;
|
|
try {
|
|
getContent(scanner);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
test.isTrue(error);
|
|
if (messageContains)
|
|
test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message);
|
|
};
|
|
|
|
|
|
succeed('', null);
|
|
succeed('^^^</', null);
|
|
succeed('abc', 'abc');
|
|
succeed('abc^^^</x>', 'abc');
|
|
succeed('a<b', ['a', CharRef({html: '<', str: '<'}), 'b']);
|
|
succeed('<!-- x -->', Comment(' x '));
|
|
succeed('∾̳', CharRef({html: '∾̳', str: '\u223e\u0333'}));
|
|
succeed('𝕫', CharRef({html: '𝕫', str: '\ud835\udd6b'}));
|
|
succeed('&&>&g>;', ['&&>&g', CharRef({html: '>', str: '>'}), ';']);
|
|
|
|
// Can't have an unescaped `&` if followed by certain names like `gt`
|
|
fatal('>&');
|
|
// tests for other failure cases
|
|
fatal('<');
|
|
|
|
succeed('<br>', BR());
|
|
succeed('<br/>', BR());
|
|
fatal('<div/>', 'self-close');
|
|
|
|
succeed('<hr id=foo>', HR({id:'foo'}));
|
|
succeed('<hr id=<foo>>', HR({id:[CharRef({html:'<', str:'<'}),
|
|
'foo',
|
|
CharRef({html:'>', str:'>'})]}));
|
|
succeed('<input selected>', INPUT({selected: ''}));
|
|
succeed('<br x=&&&>', BR({x: '&&&'}));
|
|
succeed('<br><br><br>', [BR(), BR(), BR()]);
|
|
succeed('aaa<br>\nbbb<br>\nccc<br>', ['aaa', BR(), '\nbbb', BR(), '\nccc', BR()]);
|
|
|
|
succeed('<a></a>', A());
|
|
fatal('<');
|
|
fatal('<a');
|
|
fatal('<a>');
|
|
fatal('<a><');
|
|
fatal('<a></');
|
|
fatal('<a></a');
|
|
|
|
succeed('<a href="http://www.apple.com/">Apple</a>',
|
|
A({href: "http://www.apple.com/"}, 'Apple'));
|
|
|
|
(function () {
|
|
var A = HTML.getTag('A');
|
|
var B = HTML.getTag('B');
|
|
var C = HTML.getTag('C');
|
|
var D = HTML.getTag('D');
|
|
|
|
succeed('<a>1<b>2<c>3<d>4</d>5</c>6</b>7</a>8',
|
|
[A('1', B('2', C('3', D('4'), '5'), '6'), '7'), '8']);
|
|
})();
|
|
|
|
fatal('<b>hello <i>there</b> world</i>');
|
|
|
|
// XXX support implied end tags in cases allowed by the spec
|
|
fatal('<p>');
|
|
|
|
fatal('<a>Foo</a/>');
|
|
fatal('<a>Foo</a b=c>');
|
|
|
|
succeed('<textarea>asdf</textarea>', TEXTAREA("asdf"));
|
|
succeed('<textarea x=y>asdf</textarea>', TEXTAREA({x: "y"}, "asdf"));
|
|
succeed('<textarea><p></textarea>', TEXTAREA("<p>"));
|
|
succeed('<textarea>a&b</textarea>',
|
|
TEXTAREA("a", CharRef({html: '&', str: '&'}), "b"));
|
|
succeed('<textarea></textarea</textarea>', TEXTAREA("</textarea"));
|
|
// absorb up to one initial newline, as per HTML parsing spec
|
|
succeed('<textarea>\n</textarea>', TEXTAREA());
|
|
succeed('<textarea>\nasdf</textarea>', TEXTAREA("asdf"));
|
|
succeed('<textarea>\n\nasdf</textarea>', TEXTAREA("\nasdf"));
|
|
succeed('<textarea>\n\n</textarea>', TEXTAREA("\n"));
|
|
succeed('<textarea>\nasdf\n</textarea>', TEXTAREA("asdf\n"));
|
|
succeed('<textarea><!-- --></textarea>', TEXTAREA("<!-- -->"));
|
|
succeed('<tExTaReA>asdf</TEXTarea>', TEXTAREA("asdf"));
|
|
fatal('<textarea>asdf');
|
|
fatal('<textarea>asdf</textarea');
|
|
fatal('<textarea>&davidgreenspan;</textarea>');
|
|
succeed('<textarea>&</textarea>', TEXTAREA("&"));
|
|
succeed('<textarea></textarea \n<</textarea \n>asdf',
|
|
[TEXTAREA("</textarea \n<"), "asdf"]);
|
|
|
|
});
|
|
|
|
Tinytest.add("html - parseFragment", function (test) {
|
|
test.equal(HTML.toJS(HTML.parseFragment("<div><p id=foo>Hello</p></div>")),
|
|
HTML.toJS(DIV(P({id:'foo'}, 'Hello'))));
|
|
|
|
test.throws(function() {
|
|
HTML.parseFragment('asdf</a>');
|
|
});
|
|
|
|
(function () {
|
|
var p = HTML.parseFragment('<p></p>');
|
|
test.equal(p.tagName, 'P');
|
|
test.equal(p.attrs, null);
|
|
test.isTrue(p instanceof HTML.Tag);
|
|
test.equal(p.children.length, 0);
|
|
})();
|
|
|
|
(function () {
|
|
var p = HTML.parseFragment('<p>x</p>');
|
|
test.equal(p.tagName, 'P');
|
|
test.equal(p.attrs, null);
|
|
test.isTrue(p instanceof HTML.Tag);
|
|
test.equal(p.children.length, 1);
|
|
test.equal(p.children[0], 'x');
|
|
})();
|
|
|
|
(function () {
|
|
var p = HTML.parseFragment('<p>xA</p>');
|
|
test.equal(p.tagName, 'P');
|
|
test.equal(p.attrs, null);
|
|
test.isTrue(p instanceof HTML.Tag);
|
|
test.equal(p.children.length, 2);
|
|
test.equal(p.children[0], 'x');
|
|
|
|
test.isTrue(p.children[1] instanceof HTML.CharRef);
|
|
test.equal(p.children[1].html, 'A');
|
|
test.equal(p.children[1].str, 'A');
|
|
})();
|
|
|
|
(function () {
|
|
var pp = HTML.parseFragment('<p>x</p><p>y</p>');
|
|
test.isTrue(pp instanceof Array);
|
|
test.equal(pp.length, 2);
|
|
|
|
test.equal(pp[0].tagName, 'P');
|
|
test.equal(pp[0].attrs, null);
|
|
test.isTrue(pp[0] instanceof HTML.Tag);
|
|
test.equal(pp[0].children.length, 1);
|
|
test.equal(pp[0].children[0], 'x');
|
|
|
|
test.equal(pp[1].tagName, 'P');
|
|
test.equal(pp[1].attrs, null);
|
|
test.isTrue(pp[1] instanceof HTML.Tag);
|
|
test.equal(pp[1].children.length, 1);
|
|
test.equal(pp[1].children[0], 'y');
|
|
})();
|
|
|
|
});
|
|
|
|
Tinytest.add("html - getSpecialTag", function (test) {
|
|
|
|
// match a simple tag consisting of `{{`, an optional `!`, one
|
|
// or more ASCII letters or spaces, and a closing `}}`.
|
|
var mustache = /^\{\{(!?[a-zA-Z 0-9]+)\}\}/;
|
|
|
|
// This implementation of `getSpecialTag` looks for "{{" and if it
|
|
// finds it, it will match the regex above or fail fatally trying.
|
|
// The object it returns is opaque to the tokenizer/parser and can
|
|
// be anything we want.
|
|
var getSpecialTag = function (scanner, position) {
|
|
if (! (scanner.peek() === '{' && // one-char peek is just an optimization
|
|
scanner.rest().slice(0, 2) === '{{'))
|
|
return null;
|
|
|
|
var match = mustache.exec(scanner.rest());
|
|
if (! match)
|
|
scanner.fatal("Bad mustache");
|
|
|
|
scanner.pos += match[0].length;
|
|
|
|
if (match[1].charAt(0) === '!')
|
|
return null; // `{{!foo}}` is like a comment
|
|
|
|
return { stuff: match[1] };
|
|
};
|
|
|
|
|
|
|
|
var succeed = function (input, expected) {
|
|
var endPos = input.indexOf('^^^');
|
|
if (endPos < 0)
|
|
endPos = input.length;
|
|
|
|
var scanner = new Scanner(input.replace('^^^', ''));
|
|
scanner.getSpecialTag = getSpecialTag;
|
|
var result;
|
|
try {
|
|
result = getContent(scanner);
|
|
} catch (e) {
|
|
result = String(e);
|
|
}
|
|
test.equal(scanner.pos, endPos);
|
|
test.equal(HTML.toJS(result), HTML.toJS(expected));
|
|
};
|
|
|
|
var fatal = function (input, messageContains) {
|
|
var scanner = new Scanner(input);
|
|
scanner.getSpecialTag = getSpecialTag;
|
|
var error;
|
|
try {
|
|
getContent(scanner);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
test.isTrue(error);
|
|
if (messageContains)
|
|
test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message);
|
|
};
|
|
|
|
|
|
succeed('{{foo}}', Special({stuff: 'foo'}));
|
|
|
|
succeed('<a href=http://www.apple.com/>{{foo}}</a>',
|
|
A({href: "http://www.apple.com/"}, Special({stuff: 'foo'})));
|
|
|
|
// tags not parsed in comments
|
|
succeed('<!--{{foo}}-->', Comment("{{foo}}"));
|
|
succeed('<!--{{foo-->', Comment("{{foo"));
|
|
|
|
succeed('&am{{foo}}p;', ['&am', Special({stuff: 'foo'}), 'p;']);
|
|
|
|
// can't start a mustache and not finish it
|
|
fatal('{{foo');
|
|
fatal('<a>{{</a>');
|
|
|
|
// no mustache allowed in tag name
|
|
fatal('<{{a}}>');
|
|
fatal('<{{a}}b>');
|
|
fatal('<a{{b}}>');
|
|
|
|
// single curly brace is no biggie
|
|
succeed('a{b', 'a{b');
|
|
succeed('<br x={ />', BR({x:'{'}));
|
|
succeed('<br x={foo} />', BR({x:'{foo}'}));
|
|
|
|
succeed('<br {{x}}>', BR({$specials: [Special({stuff: 'x'})]}));
|
|
succeed('<br {{x}} {{y}}>', BR({$specials: [Special({stuff: 'x'}),
|
|
Special({stuff: 'y'})]}));
|
|
succeed('<br {{x}} y>', BR({$specials: [Special({stuff: 'x'})], y:''}));
|
|
fatal('<br {{x}}y>');
|
|
fatal('<br {{x}}=y>');
|
|
succeed('<br x={{y}} z>', BR({x: Special({stuff: 'y'}), z: ''}));
|
|
succeed('<br x=y{{z}}w>', BR({x: ['y', Special({stuff: 'z'}), 'w']}));
|
|
succeed('<br x="y{{z}}w">', BR({x: ['y', Special({stuff: 'z'}), 'w']}));
|
|
succeed('<br x="y {{z}}{{w}} v">', BR({x: ['y ', Special({stuff: 'z'}),
|
|
Special({stuff: 'w'}), ' v']}));
|
|
// Slash is parsed as part of unquoted attribute! This is consistent with
|
|
// the HTML tokenization spec. It seems odd for some inputs but is probably
|
|
// for cases like `<a href=http://foo.com/>` or `<a href=/foo/>`.
|
|
succeed('<br x={{y}}/>', BR({x: [Special({stuff: 'y'}), '/']}));
|
|
succeed('<br x={{z}}{{w}}>', BR({x: [Special({stuff: 'z'}),
|
|
Special({stuff: 'w'})]}));
|
|
fatal('<br x="y"{{z}}>');
|
|
|
|
succeed('<br x=&>', BR({x:CharRef({html: '&', str: '&'})}));
|
|
|
|
|
|
// check tokenization of stache tags with spaces
|
|
succeed('<br {{x 1}}>', BR({$specials: [Special({stuff: 'x 1'})]}));
|
|
succeed('<br {{x 1}} {{y 2}}>', BR({$specials: [Special({stuff: 'x 1'}),
|
|
Special({stuff: 'y 2'})]}));
|
|
succeed('<br {{x 1}} y>', BR({$specials: [Special({stuff: 'x 1'})], y:''}));
|
|
fatal('<br {{x 1}}y>');
|
|
fatal('<br {{x 1}}=y>');
|
|
succeed('<br x={{y 2}} z>', BR({x: Special({stuff: 'y 2'}), z: ''}));
|
|
succeed('<br x=y{{z 3}}w>', BR({x: ['y', Special({stuff: 'z 3'}), 'w']}));
|
|
succeed('<br x="y{{z 3}}w">', BR({x: ['y', Special({stuff: 'z 3'}), 'w']}));
|
|
succeed('<br x="y {{z 3}}{{w 4}} v">', BR({x: ['y ', Special({stuff: 'z 3'}),
|
|
Special({stuff: 'w 4'}), ' v']}));
|
|
succeed('<br x={{y 2}}/>', BR({x: [Special({stuff: 'y 2'}), '/']}));
|
|
succeed('<br x={{z 3}}{{w 4}}>', BR({x: [Special({stuff: 'z 3'}),
|
|
Special({stuff: 'w 4'})]}));
|
|
|
|
succeed('<p></p>', P());
|
|
|
|
succeed('x{{foo}}{{bar}}y', ['x', Special({stuff: 'foo'}),
|
|
Special({stuff: 'bar'}), 'y']);
|
|
succeed('x{{!foo}}{{!bar}}y', 'xy');
|
|
succeed('x{{!foo}}{{bar}}y', ['x', Special({stuff: 'bar'}), 'y']);
|
|
succeed('x{{foo}}{{!bar}}y', ['x', Special({stuff: 'foo'}), 'y']);
|
|
|
|
succeed('', null);
|
|
succeed('{{!foo}}', null);
|
|
|
|
});
|