2017-09-29 22:39:37 +00:00
|
|
|
import 'package:code_buffer/code_buffer.dart';
|
|
|
|
import 'package:jael/jael.dart' as jael;
|
|
|
|
import 'package:symbol_table/symbol_table.dart';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
2021-04-29 07:21:31 +00:00
|
|
|
void main() {
|
2017-09-29 22:39:37 +00:00
|
|
|
test('attribute binding', () {
|
|
|
|
const template = '''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Hello</h1>
|
2017-10-16 22:48:35 +00:00
|
|
|
<img ready="always" data-img-src=profile['avatar'] />
|
2017-10-17 05:14:51 +00:00
|
|
|
<input name="csrf_token" type="hidden" value=csrf_token>
|
2017-09-29 22:39:37 +00:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
jael.Document? document;
|
|
|
|
late SymbolTable scope;
|
2017-10-16 22:48:35 +00:00
|
|
|
|
|
|
|
try {
|
2019-07-29 22:01:24 +00:00
|
|
|
document = jael.parseDocument(template, sourceUrl: 'test.jael');
|
2019-07-29 22:12:52 +00:00
|
|
|
scope = SymbolTable<dynamic>(values: {
|
2017-10-17 05:14:51 +00:00
|
|
|
'csrf_token': 'foo',
|
2017-10-16 22:48:35 +00:00
|
|
|
'profile': {
|
|
|
|
'avatar': 'thosakwe.png',
|
|
|
|
}
|
|
|
|
});
|
2018-04-03 05:04:34 +00:00
|
|
|
} on jael.JaelError catch (e) {
|
2017-10-16 22:48:35 +00:00
|
|
|
print(e);
|
|
|
|
print(e.stackTrace);
|
|
|
|
}
|
2017-09-29 22:39:37 +00:00
|
|
|
|
2017-10-16 22:48:35 +00:00
|
|
|
expect(document, isNotNull);
|
2021-04-28 00:37:33 +00:00
|
|
|
const jael.Renderer().render(document!, buf, scope);
|
2017-09-29 22:39:37 +00:00
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
buf.toString(),
|
|
|
|
'''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>
|
|
|
|
Hello
|
|
|
|
</h1>
|
2017-10-16 22:48:35 +00:00
|
|
|
<img ready="always" data-img-src="thosakwe.png">
|
2017-10-17 05:14:51 +00:00
|
|
|
<input name="csrf_token" type="hidden" value="foo">
|
2017-09-29 22:39:37 +00:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
'''
|
|
|
|
.trim());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('interpolation', () {
|
|
|
|
const template = '''
|
|
|
|
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Pokémon</h1>
|
|
|
|
{{ pokemon.name }} - {{ pokemon.type }}
|
|
|
|
<img>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2019-07-29 22:01:24 +00:00
|
|
|
//jael.scan(template, sourceUrl: 'test.jael').tokens.forEach(print);
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable<dynamic>(values: {
|
2017-09-29 22:39:37 +00:00
|
|
|
'pokemon': const _Pokemon('Darkrai', 'Dark'),
|
|
|
|
});
|
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
2018-04-03 05:04:34 +00:00
|
|
|
buf.toString().replaceAll('\n', '').replaceAll(' ', '').trim(),
|
2017-09-29 22:39:37 +00:00
|
|
|
'''
|
|
|
|
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>
|
|
|
|
Pokémon
|
|
|
|
</h1>
|
|
|
|
Darkrai - Dark
|
2018-04-03 18:07:34 +00:00
|
|
|
<img/>
|
2017-09-29 22:39:37 +00:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
'''
|
2018-04-03 05:04:34 +00:00
|
|
|
.replaceAll('\n', '')
|
|
|
|
.replaceAll(' ', '')
|
2017-09-29 22:39:37 +00:00
|
|
|
.trim());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('for loop', () {
|
|
|
|
const template = '''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Pokémon</h1>
|
|
|
|
<ul>
|
2018-06-27 23:54:43 +00:00
|
|
|
<li for-each=starters as="starter" index-as="idx">#{{ idx }} {{ starter.name }} - {{ starter.type }}</li>
|
2017-09-29 22:39:37 +00:00
|
|
|
</ul>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable<dynamic>(values: {
|
2017-09-29 22:39:37 +00:00
|
|
|
'starters': starters,
|
|
|
|
});
|
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
buf.toString(),
|
|
|
|
'''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>
|
|
|
|
Pokémon
|
|
|
|
</h1>
|
|
|
|
<ul>
|
|
|
|
<li>
|
2018-06-27 23:54:43 +00:00
|
|
|
#0 Bulbasaur - Grass
|
2017-09-29 22:39:37 +00:00
|
|
|
</li>
|
|
|
|
<li>
|
2018-06-27 23:54:43 +00:00
|
|
|
#1 Charmander - Fire
|
2017-09-29 22:39:37 +00:00
|
|
|
</li>
|
|
|
|
<li>
|
2018-06-27 23:54:43 +00:00
|
|
|
#2 Squirtle - Water
|
2017-09-29 22:39:37 +00:00
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
'''
|
|
|
|
.trim());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('conditional', () {
|
|
|
|
const template = '''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Conditional</h1>
|
|
|
|
<b if=starters.isEmpty>Empty</b>
|
|
|
|
<b if=starters.isNotEmpty>Not empty</b>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable<dynamic>(values: {
|
2017-09-29 22:39:37 +00:00
|
|
|
'starters': starters,
|
|
|
|
});
|
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
buf.toString(),
|
|
|
|
'''
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>
|
|
|
|
Conditional
|
|
|
|
</h1>
|
|
|
|
<b>
|
|
|
|
Not empty
|
|
|
|
</b>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
'''
|
|
|
|
.trim());
|
|
|
|
});
|
2017-10-02 15:46:00 +00:00
|
|
|
|
|
|
|
test('declare', () {
|
|
|
|
const template = '''
|
|
|
|
<div>
|
|
|
|
<declare one=1 two=2 three=3>
|
|
|
|
<ul>
|
|
|
|
<li>{{one}}</li>
|
|
|
|
<li>{{two}}</li>
|
|
|
|
<li>{{three}}</li>
|
|
|
|
</ul>
|
|
|
|
<ul>
|
|
|
|
<declare three=4>
|
|
|
|
<li>{{one}}</li>
|
|
|
|
<li>{{two}}</li>
|
|
|
|
<li>{{three}}</li>
|
|
|
|
</declare>
|
|
|
|
</ul>
|
|
|
|
</declare>
|
|
|
|
</div>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable();
|
2017-10-02 15:46:00 +00:00
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
buf.toString(),
|
|
|
|
'''
|
|
|
|
<div>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
1
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
2
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
3
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
1
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
2
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
4
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
'''
|
|
|
|
.trim());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('unescaped attr/interp', () {
|
|
|
|
const template = '''
|
|
|
|
<div>
|
|
|
|
<img src!="<SCARY XSS>" />
|
|
|
|
{{- "<MORE SCARY XSS>" }}
|
|
|
|
</div>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable();
|
2017-10-02 15:46:00 +00:00
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
2018-04-03 18:07:34 +00:00
|
|
|
buf.toString().replaceAll('\n', '').replaceAll(' ', '').trim(),
|
2017-10-02 15:46:00 +00:00
|
|
|
'''
|
|
|
|
<div>
|
|
|
|
<img src="<SCARY XSS>">
|
|
|
|
<MORE SCARY XSS>
|
|
|
|
</div>
|
|
|
|
'''
|
2018-04-03 18:07:34 +00:00
|
|
|
.replaceAll('\n', '')
|
|
|
|
.replaceAll(' ', '')
|
2017-10-02 15:46:00 +00:00
|
|
|
.trim());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('quoted attribute name', () {
|
|
|
|
const template = '''
|
|
|
|
<button '(click)'="myEventHandler(\$event)"></button>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable();
|
2017-10-02 15:46:00 +00:00
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
buf.toString(),
|
|
|
|
'''
|
|
|
|
<button (click)="myEventHandler(\$event)">
|
|
|
|
</button>
|
|
|
|
'''
|
|
|
|
.trim());
|
|
|
|
});
|
2017-10-02 16:29:13 +00:00
|
|
|
|
|
|
|
test('switch', () {
|
|
|
|
const template = '''
|
|
|
|
<switch value=account.isDisabled>
|
|
|
|
<case value=true>
|
|
|
|
BAN HAMMER LOLOL
|
|
|
|
</case>
|
|
|
|
<case value=false>
|
|
|
|
You are in good standing.
|
|
|
|
</case>
|
|
|
|
<default>
|
|
|
|
Weird...
|
|
|
|
</default>
|
|
|
|
</switch>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable<dynamic>(values: {
|
|
|
|
'account': _Account(isDisabled: true),
|
2017-10-02 16:29:13 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(buf.toString().trim(), 'BAN HAMMER LOLOL');
|
|
|
|
});
|
|
|
|
|
|
|
|
test('default', () {
|
|
|
|
const template = '''
|
|
|
|
<switch value=account.isDisabled>
|
|
|
|
<case value=true>
|
|
|
|
BAN HAMMER LOLOL
|
|
|
|
</case>
|
|
|
|
<case value=false>
|
|
|
|
You are in good standing.
|
|
|
|
</case>
|
|
|
|
<default>
|
|
|
|
Weird...
|
|
|
|
</default>
|
|
|
|
</switch>
|
|
|
|
''';
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
var buf = CodeBuffer();
|
2021-04-28 00:37:33 +00:00
|
|
|
var document = jael.parseDocument(template, sourceUrl: 'test.jael')!;
|
2019-07-29 22:12:52 +00:00
|
|
|
var scope = SymbolTable<dynamic>(values: {
|
|
|
|
'account': _Account(isDisabled: null),
|
2017-10-02 16:29:13 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const jael.Renderer().render(document, buf, scope);
|
|
|
|
print(buf);
|
|
|
|
|
|
|
|
expect(buf.toString().trim(), 'Weird...');
|
|
|
|
});
|
2017-09-29 22:39:37 +00:00
|
|
|
}
|
|
|
|
|
2019-07-29 22:12:52 +00:00
|
|
|
const List<_Pokemon> starters = [
|
|
|
|
_Pokemon('Bulbasaur', 'Grass'),
|
|
|
|
_Pokemon('Charmander', 'Fire'),
|
|
|
|
_Pokemon('Squirtle', 'Water'),
|
2017-09-29 22:39:37 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
class _Pokemon {
|
|
|
|
final String name, type;
|
|
|
|
|
|
|
|
const _Pokemon(this.name, this.type);
|
|
|
|
}
|
2017-10-02 16:29:13 +00:00
|
|
|
|
|
|
|
class _Account {
|
2021-04-28 00:37:33 +00:00
|
|
|
final bool? isDisabled;
|
2017-10-02 16:29:13 +00:00
|
|
|
|
|
|
|
_Account({this.isDisabled});
|
|
|
|
}
|