blob: 3aadd203c394f38547b70fdfeef5d5edf39796ed [file] [log] [blame]
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:angular_ast/angular_ast.dart';
import 'package:angular_analyzer_plugin/src/from_file_prefixed_error.dart';
import 'package:angular_analyzer_plugin/src/model.dart';
import 'package:angular_analyzer_plugin/src/selector.dart';
import 'package:angular_analyzer_plugin/errors.dart';
import 'package:angular_analyzer_plugin/ast.dart';
import 'package:angular_analyzer_plugin/src/view_extraction.dart';
import 'package:angular_analyzer_plugin/src/directive_linking.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:test/test.dart';
import 'abstract_angular.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(AngularParseHtmlTest);
defineReflectiveTests(BuildStandardHtmlComponentsTest);
defineReflectiveTests(BuildStandardAngularTest);
defineReflectiveTests(GatherAnnotationsTest);
defineReflectiveTests(BuildUnitViewsTest);
defineReflectiveTests(ResolveDartTemplatesTest);
defineReflectiveTests(LinkDirectivesTest);
defineReflectiveTests(ResolveHtmlTemplatesTest);
defineReflectiveTests(ResolveHtmlTemplateTest);
});
}
@reflectiveTest
class AngularParseHtmlTest extends AbstractAngularTest {
// ignore: non_constant_identifier_names
void test_perform() {
final code = r'''
<!DOCTYPE html>
<html>
<head>
<title> test page </title>
</head>
<body>
<h1 myAttr='my value'>Test</h1>
</body>
</html>
''';
final source = newSource('/test.html', code);
final tplParser = new TemplateParser()..parse(code, source);
expect(tplParser.parseErrors, isEmpty);
// HTML_DOCUMENT
{
final asts = tplParser.rawAst;
expect(asts, isNotNull);
// verify that attributes are not lower-cased
final element = asts[1].childNodes[3].childNodes[1] as ElementAst;
expect(element.attributes.length, 1);
expect(element.attributes[0].name, 'myAttr');
expect(element.attributes[0].value, 'my value');
}
}
// ignore: non_constant_identifier_names
void test_perform_noDocType() {
final code = r'''
<div>AAA</div>
<span>BBB</span>
''';
final source = newSource('/test.html', code);
final tplParser = new TemplateParser()..parse(code, source);
// validate Document
{
final asts = tplParser.rawAst;
expect(asts, isNotNull);
expect(asts.length, 4);
expect((asts[0] as ElementAst).name, 'div');
expect((asts[2] as ElementAst).name, 'span');
}
// it's OK to don't have DOCTYPE
expect(tplParser.parseErrors, isEmpty);
}
// ignore: non_constant_identifier_names
// ignore: non_constant_identifier_names
void test_perform_noDocType_with_dangling_unclosed_tag() {
final code = r'''
<div>AAA</div>
<span>BBB</span>
<di''';
final source = newSource('/test.html', code);
final tplParser = new TemplateParser()..parse(code, source);
// quick validate Document
{
final asts = tplParser.rawAst;
expect(asts, isNotNull);
expect(asts.length, 5);
expect((asts[0] as ElementAst).name, 'div');
expect((asts[2] as ElementAst).name, 'span');
expect((asts[4] as ElementAst).name, 'di');
}
}
}
@reflectiveTest
class BuildStandardHtmlComponentsTest extends AbstractAngularTest {
// ignore: non_constant_identifier_names
// ignore: non_constant_identifier_names
Future test_perform() async {
final stdhtml = await angularDriver.getStandardHtml();
// validate
final map = stdhtml.components;
expect(map, isNotNull);
// a
{
final component = map['a'];
expect(component, isNotNull);
expect(component.classElement.displayName, 'AnchorElement');
expect(component.selector.toString(), 'a');
final inputs = component.inputs;
final outputElements = component.outputs;
{
final input = inputs.singleWhere((i) => i.name == 'href');
expect(input, isNotNull);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals("String"));
expect(input.securityContext, isNotNull);
expect(input.securityContext.safeType.toString(), equals('SafeUrl'));
expect(input.securityContext.sanitizationAvailable, equals(true));
}
expect(outputElements, hasLength(0));
expect(inputs.where((i) => i.name == '_privateField'), hasLength(0));
}
// button
{
final component = map['button'];
expect(component, isNotNull);
expect(component.classElement.displayName, 'ButtonElement');
expect(component.selector.toString(), 'button');
final inputs = component.inputs;
final outputElements = component.outputs;
{
final input = inputs.singleWhere((i) => i.name == 'autofocus');
expect(input, isNotNull);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals("bool"));
expect(input.securityContext, isNull);
}
expect(outputElements, hasLength(0));
}
// iframe
{
final component = map['iframe'];
expect(component, isNotNull);
expect(component.classElement.displayName, 'IFrameElement');
expect(component.selector.toString(), 'iframe');
final inputs = component.inputs;
{
final input = inputs.singleWhere((i) => i.name == 'src');
expect(input, isNotNull);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals("String"));
expect(input.securityContext, isNotNull);
expect(input.securityContext.safeType.toString(),
equals('SafeResourceUrl'));
expect(input.securityContext.sanitizationAvailable, equals(false));
}
}
// input
{
final component = map['input'];
expect(component, isNotNull);
expect(component.classElement.displayName, 'InputElement');
expect(component.selector.toString(), 'input');
final outputElements = component.outputs;
expect(outputElements, hasLength(0));
}
// body is one of the few elements with special events
{
final component = map['body'];
expect(component, isNotNull);
expect(component.classElement.displayName, 'BodyElement');
expect(component.selector.toString(), 'body');
final outputElements = component.outputs;
expect(outputElements, hasLength(1));
{
final output = outputElements[0];
expect(output.name, equals("unload"));
expect(output.getter, isNotNull);
expect(output.eventType, isNotNull);
}
}
// h1, h2, h3
expect(map['h1'], isNotNull);
expect(map['h2'], isNotNull);
expect(map['h3'], isNotNull);
// has no mention of 'option' in the source, is hardcoded
expect(map['option'], isNotNull);
// <template> is special, not actually a TemplateElement
expect(map['template'], isNull);
// <audio> is a "specialElementClass", its ctor isn't analyzable.
expect(map['audio'], isNotNull);
}
// ignore: non_constant_identifier_names
Future test_buildStandardHtmlEvents() async {
final stdhtml = await angularDriver.getStandardHtml();
final outputElements = stdhtml.events;
{
// This one is important because it proves we're using @DomAttribute
// to generate the output name and not the method in the sdk.
final outputElement = outputElements['keyup'];
expect(outputElement, isNotNull);
expect(outputElement.getter, isNotNull);
expect(outputElement.eventType, isNotNull);
}
{
final outputElement = outputElements['cut'];
expect(outputElement, isNotNull);
expect(outputElement.getter, isNotNull);
expect(outputElement.eventType, isNotNull);
}
{
final outputElement = outputElements['click'];
expect(outputElement, isNotNull);
expect(outputElement.getter, isNotNull);
expect(outputElement.eventType, isNotNull);
expect(outputElement.eventType.toString(), equals('MouseEvent'));
}
{
final outputElement = outputElements['change'];
expect(outputElement, isNotNull);
expect(outputElement.getter, isNotNull);
expect(outputElement.eventType, isNotNull);
}
{
// used to happen from "id" which got truncated by 'on'.length
final outputElement = outputElements[''];
expect(outputElement, isNull);
}
{
// used to happen from "hidden" which got truncated by 'on'.length
final outputElement = outputElements['dden'];
expect(outputElement, isNull);
}
}
// ignore: non_constant_identifier_names
Future test_buildStandardHtmlAttributes() async {
final stdhtml = await angularDriver.getStandardHtml();
final inputElements = stdhtml.attributes;
{
final input = inputElements['tabIndex'];
expect(input, isNotNull);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals("int"));
}
{
final input = inputElements['hidden'];
expect(input, isNotNull);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals("bool"));
}
{
final input = inputElements['innerHtml'];
expect(input, isNotNull);
expect(identical(input, inputElements['innerHTML']), true);
expect(input.setter, isNotNull);
expect(input.setterType.toString(), equals('String'));
expect(input.securityContext, isNotNull);
expect(input.securityContext.safeType.toString(), equals('SafeHtml'));
expect(input.securityContext.sanitizationAvailable, equals(true));
}
}
}
@reflectiveTest
class BuildStandardAngularTest extends AbstractAngularTest {
// ignore: non_constant_identifier_names
Future test_perform() async {
final ng = await angularDriver.getStandardAngular();
// validate
expect(ng, isNotNull);
expect(ng.templateRef, isNotNull);
expect(ng.elementRef, isNotNull);
expect(ng.queryList, isNotNull);
expect(ng.pipeTransform, isNotNull);
expect(ng.component, isNotNull);
}
// ignore: non_constant_identifier_names
Future test_securitySchema() async {
final ng = await angularDriver.getStandardAngular();
// validate
expect(ng, isNotNull);
expect(ng.securitySchema, isNotNull);
final imgSrcSecurity = ng.securitySchema.lookup('img', 'src');
expect(imgSrcSecurity, isNotNull);
expect(imgSrcSecurity.safeType.toString(), 'SafeUrl');
expect(imgSrcSecurity.sanitizationAvailable, true);
final aHrefSecurity = ng.securitySchema.lookup('a', 'href');
expect(aHrefSecurity, isNotNull);
expect(aHrefSecurity.safeType.toString(), 'SafeUrl');
expect(aHrefSecurity.sanitizationAvailable, true);
final innerHtmlSecurity = ng.securitySchema.lookupGlobal('innerHTML');
expect(innerHtmlSecurity, isNotNull);
expect(innerHtmlSecurity.safeType.toString(), 'SafeHtml');
expect(innerHtmlSecurity.sanitizationAvailable, true);
final iframeSrcdocSecurity = ng.securitySchema.lookup('iframe', 'srcdoc');
expect(iframeSrcdocSecurity, isNotNull);
expect(iframeSrcdocSecurity.safeType.toString(), 'SafeHtml');
expect(iframeSrcdocSecurity.sanitizationAvailable, true);
final styleSecurity = ng.securitySchema.lookupGlobal('style');
expect(styleSecurity, isNotNull);
expect(styleSecurity.safeType.toString(), 'SafeStyle');
expect(styleSecurity.sanitizationAvailable, true);
final iframeSrcSecurity = ng.securitySchema.lookup('iframe', 'src');
expect(iframeSrcSecurity, isNotNull);
expect(iframeSrcSecurity.safeType.toString(), 'SafeResourceUrl');
expect(iframeSrcSecurity.sanitizationAvailable, false);
final scriptSrcSecurity = ng.securitySchema.lookup('script', 'src');
expect(scriptSrcSecurity, isNotNull);
expect(scriptSrcSecurity.safeType.toString(), 'SafeResourceUrl');
expect(scriptSrcSecurity.sanitizationAvailable, false);
}
}
@reflectiveTest
class GatherAnnotationsTest extends AbstractAngularTest {
List<AbstractDirective> directives;
List<Pipe> pipes;
List<AnalysisError> errors;
Future getDirectives(final source) async {
final dartResult = await dartDriver.getResult(source.fullName);
fillErrorListener(dartResult.errors);
final result = await angularDriver.getAngularTopLevels(source.fullName);
directives = result.directives;
pipes = result.pipes;
errors = result.errors;
fillErrorListener(errors);
}
// ignore: non_constant_identifier_names
Future test_Component() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'comp-a', template:'')
class ComponentA {
}
@Component(selector: 'comp-b', template:'')
class ComponentB {
}
''');
await getDirectives(source);
expect(directives, hasLength(2));
{
final component = directives[0];
expect(component, const isInstanceOf<Component>());
{
final selector = component.selector;
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'comp-a');
}
{
expect(component.elementTags, hasLength(1));
final selector = component.elementTags[0];
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'comp-a');
}
}
{
final component = directives[1];
expect(component, const isInstanceOf<Component>());
{
final selector = component.selector;
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'comp-b');
}
{
expect(component.elementTags, hasLength(1));
final selector = component.elementTags[0];
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'comp-b');
}
}
}
// ignore: non_constant_identifier_names
Future test_Directive() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a')
class DirectiveA {
}
@Directive(selector: 'dir-b')
class DirectiveB {
}
''');
await getDirectives(source);
expect(directives, hasLength(2));
{
final directive = directives[0];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'dir-a');
}
{
expect(directive.elementTags, hasLength(1));
final selector = directive.elementTags[0];
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'dir-a');
}
}
{
final directive = directives[1];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'dir-b');
}
{
expect(directive.elementTags, hasLength(1));
final selector = directive.elementTags[0];
expect(selector, const isInstanceOf<ElementNameSelector>());
expect(selector.toString(), 'dir-b');
}
}
}
// ignore: non_constant_identifier_names
Future test_Directive_elementTags_OrSelector() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a1, dir-a2, dir-a3')
class DirectiveA {
}
@Directive(selector: 'dir-b1, dir-b2')
class DirectiveB {
}
''');
await getDirectives(source);
expect(directives, hasLength(2));
{
final directive = directives[0];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<OrSelector>());
expect((selector as OrSelector).selectors, hasLength(3));
}
{
expect(directive.elementTags, hasLength(3));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-a1');
expect(directive.elementTags[1],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[1].toString(), 'dir-a2');
expect(directive.elementTags[2],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[2].toString(), 'dir-a3');
}
}
{
final directive = directives[1];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<OrSelector>());
expect((selector as OrSelector).selectors, hasLength(2));
}
{
expect(directive.elementTags, hasLength(2));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-b1');
expect(directive.elementTags[1],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[1].toString(), 'dir-b2');
}
}
}
// ignore: non_constant_identifier_names
Future test_Directive_elementTags_AndSelector() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a.myClass[myAttr]')
class DirectiveA {
}
@Directive(selector: 'dir-b[myAttr]')
class DirectiveB {
}
''');
await getDirectives(source);
expect(directives, hasLength(2));
{
final directive = directives[0];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<AndSelector>());
expect((selector as AndSelector).selectors, hasLength(3));
}
{
expect(directive.elementTags, hasLength(1));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-a');
}
}
{
final directive = directives[1];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<AndSelector>());
expect((selector as AndSelector).selectors, hasLength(2));
}
{
expect(directive.elementTags, hasLength(1));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-b');
}
}
}
// ignore: non_constant_identifier_names
Future test_Directive_elementTags_CompoundSelector() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a1.myClass[myAttr], dir-a2.otherClass')
class DirectiveA {
}
@Directive(selector: 'dir-b1[myAttr], dir-b2')
class DirectiveB {
}
''');
await getDirectives(source);
expect(directives, hasLength(2));
{
final directive = directives[0];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<OrSelector>());
expect((selector as OrSelector).selectors, hasLength(2));
}
{
expect(directive.elementTags, hasLength(2));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-a1');
expect(directive.elementTags[1],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[1].toString(), 'dir-a2');
}
}
{
final directive = directives[1];
expect(directive, const isInstanceOf<Directive>());
{
final selector = directive.selector;
expect(selector, const isInstanceOf<OrSelector>());
expect((selector as OrSelector).selectors, hasLength(2));
}
{
expect(directive.elementTags, hasLength(2));
expect(directive.elementTags[0],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-b1');
expect(directive.elementTags[1],
const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[1].toString(), 'dir-b2');
}
}
}
// ignore: non_constant_identifier_names
Future test_FunctionalDirective() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a.myClass[myAttr]')
void directiveA() {
}
''');
await getDirectives(source);
expect(directives, hasLength(1));
final directive = directives.single;
expect(directive, const isInstanceOf<FunctionalDirective>());
expect(directive.name, "directiveA");
final selector = directive.selector;
expect(selector, const isInstanceOf<AndSelector>());
expect((selector as AndSelector).selectors, hasLength(3));
expect(directive.elementTags, hasLength(1));
expect(directive.elementTags[0], const isInstanceOf<ElementNameSelector>());
expect(directive.elementTags[0].toString(), 'dir-a');
}
// ignore: non_constant_identifier_names
Future test_FunctionalDirective_notAllowedValues() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'dir-a.myClass[myAttr]',
exportAs: 'foo')
void directiveA() {
}
''');
await getDirectives(source);
errorListener.assertErrorsWithCodes(
[AngularWarningCode.FUNCTIONAL_DIRECTIVES_CANT_BE_EXPORTED]);
}
// ignore: non_constant_identifier_names
Future test_Pipe() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Pipe('pipeA')
class PipeA extends PipeTransform {
int transform(int blah) => blah;
}
@Pipe('pipeB', pure: false)
class PipeB extends PipeTransform {
String transform(int a1, String a2, bool a3) => 'someString';
}
''');
await getDirectives(source);
expect(pipes, hasLength(2));
{
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeA');
expect(pure, true);
expect(pipe.requiredArgumentType.toString(), 'int');
expect(pipe.transformReturnType.toString(), 'int');
expect(pipe.optionalArgumentTypes, hasLength(0));
}
{
final pipe = pipes[1];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeB');
expect(pure, false);
expect(pipe.requiredArgumentType.toString(), 'int');
expect(pipe.transformReturnType.toString(), 'String');
final opArgs = pipe.optionalArgumentTypes;
expect(opArgs, hasLength(2));
expect(opArgs[0].toString(), 'String');
expect(opArgs[1].toString(), 'bool');
}
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_Pipe_error_no_pipeTransform() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Pipe('pipeA')
class PipeA {
int transform(int blah) => blah;
}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeA');
expect(pure, true);
expect(pipe.transformReturnType.toString(), 'int');
expect(pipe.requiredArgumentType.toString(), 'int');
expect(pipe.optionalArgumentTypes, hasLength(0));
errorListener.assertErrorsWithCodes(
[AngularWarningCode.PIPE_REQUIRES_PIPETRANSFORM]);
}
// ignore: non_constant_identifier_names
Future test_Pipe_error_bad_extends() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
class Trouble {}
@Pipe('pipeA')
class PipeA extends Trouble{
int transform(int blah) => blah;
}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeA');
expect(pure, true);
expect(pipe.transformReturnType.toString(), 'int');
expect(pipe.requiredArgumentType.toString(), 'int');
expect(pipe.optionalArgumentTypes, hasLength(0));
errorListener.assertErrorsWithCodes(
[AngularWarningCode.PIPE_REQUIRES_PIPETRANSFORM]);
}
// ignore: non_constant_identifier_names
Future test_Pipe_is_abstract() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
class Trouble {}
@Pipe('pipeA')
abstract class PipeA extends PipeTransform{
int transform(int blah) => blah;
}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeA');
expect(pure, true);
expect(pipe.transformReturnType.toString(), 'int');
expect(pipe.requiredArgumentType.toString(), 'int');
expect(pipe.optionalArgumentTypes, hasLength(0));
errorListener
.assertErrorsWithCodes([AngularWarningCode.PIPE_CANNOT_BE_ABSTRACT]);
}
// ignore: non_constant_identifier_names
Future test_Pipe_error_no_transform() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
class Trouble {}
@Pipe('pipeA')
class PipeA extends PipeTransform{}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
final pipeName = pipe.pipeName;
final pure = pipe.isPure;
expect(pipeName, const isInstanceOf<String>());
expect(pipeName, 'pipeA');
expect(pure, true);
expect(pipe.requiredArgumentType, null);
expect(pipe.transformReturnType, null);
expect(pipe.optionalArgumentTypes, hasLength(0));
errorListener.assertErrorsWithCodes(
[AngularWarningCode.PIPE_REQUIRES_TRANSFORM_METHOD]);
}
// ignore: non_constant_identifier_names
Future test_Pipe_error_named_args() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Pipe('pipeA')
class PipeA extends PipeTransform{
transform({named}) {}
}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
errorListener.assertErrorsWithCodes(
[AngularWarningCode.PIPE_TRANSFORM_NO_NAMED_ARGS]);
}
// ignore: non_constant_identifier_names
Future test_Pipe_allowedOptionalArgs() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Pipe('pipeA')
class PipeA extends PipeTransform{
transform([named]) {}
}
''');
await getDirectives(source);
expect(pipes, hasLength(1));
final pipe = pipes[0];
expect(pipe, const isInstanceOf<Pipe>());
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_exportAs_Component() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', exportAs: 'export-name', template:'')
class ComponentA {
}
@Component(selector: 'bbb', template:'')
class ComponentB {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
expect(directives, hasLength(2));
{
final component = getComponentByName(directives, 'ComponentA');
{
final exportAs = component.exportAs;
expect(exportAs.name, 'export-name');
expect(exportAs.nameOffset, code.indexOf('export-name'));
}
}
{
final component = getComponentByName(directives, 'ComponentB');
{
final exportAs = component.exportAs;
expect(exportAs, isNull);
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_exportAs_Directive() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]', exportAs: 'export-name')
class DirectiveA {
}
@Directive(selector: '[bbb]')
class DirectiveB {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
expect(directives, hasLength(2));
{
final directive = getDirectiveByName(directives, 'DirectiveA');
{
final exportAs = directive.exportAs;
expect(exportAs.name, 'export-name');
expect(exportAs.nameOffset, code.indexOf('export-name'));
}
}
{
final directive = getDirectiveByName(directives, 'DirectiveB');
{
final exportAs = directive.exportAs;
expect(exportAs, isNull);
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_exportAs_hasError_notStringValue() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', exportAs: 42, template:'')
class ComponentA {
}
''');
await getDirectives(source);
expect(directives, hasLength(1));
// has an error
errorListener.assertErrorsWithCodes(<ErrorCode>[
AngularWarningCode.STRING_VALUE_EXPECTED,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
]);
}
// ignore: non_constant_identifier_names
Future test_exportAs_constantStringExpressionOk() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', exportAs: 'a' + 'b', template:'')
class ComponentA {
}
''');
await getDirectives(source);
expect(directives, hasLength(1));
// has no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasError_ArgumentSelectorMissing() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(template:'')
class ComponentA {
}
''');
await getDirectives(source);
// validate
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularWarningCode.ARGUMENT_SELECTOR_MISSING]);
}
// ignore: non_constant_identifier_names
Future test_hasError_CannotParseSelector() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'a+bad selector', template: '')
class ComponentA {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.CANNOT_PARSE_SELECTOR, code, "+");
}
// ignore: non_constant_identifier_names
Future test_hasError_selector_notStringValue() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 55, template: '')
class ComponentA {
}
''');
await getDirectives(source);
// validate
errorListener.assertErrorsWithCodes(<ErrorCode>[
AngularWarningCode.STRING_VALUE_EXPECTED,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
]);
}
// ignore: non_constant_identifier_names
Future test_selector_constantExpressionOk() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'a' + '[b]', template: '')
class ComponentA {
}
''');
await getDirectives(source);
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasError_UndefinedSetter_fullSyntax() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', inputs: const ['noSetter: no-setter'], template: '')
class ComponentA {
}
''');
await getDirectives(source);
final component = directives.single;
final inputs = component.inputs;
// the bad input should NOT show up, it is not usable see github #183
expect(inputs, hasLength(0));
// validate
errorListener.assertErrorsWithCodes(
<ErrorCode>[StaticTypeWarningCode.UNDEFINED_SETTER]);
}
// ignore: non_constant_identifier_names
Future test_hasError_UndefinedSetter_shortSyntax() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', inputs: const ['noSetter'], template: '')
class ComponentA {
}
''');
await getDirectives(source);
// validate
errorListener.assertErrorsWithCodes(
<ErrorCode>[StaticTypeWarningCode.UNDEFINED_SETTER]);
}
// ignore: non_constant_identifier_names
Future test_hasError_UndefinedSetter_shortSyntax_noInputMade() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', inputs: const ['noSetter'], template: '')
class ComponentA {
}
''');
await getDirectives(source);
final component = directives.single;
final inputs = component.inputs;
// the bad input should NOT show up, it is not usable see github #183
expect(inputs, hasLength(0));
// validate
errorListener.assertErrorsWithCodes(
<ErrorCode>[StaticTypeWarningCode.UNDEFINED_SETTER]);
}
// ignore: non_constant_identifier_names
Future test_inputs() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>',
inputs: const ['leadingText', 'trailingText: tailText'])
class MyComponent {
String leadingText;
int trailingText;
@Input()
bool firstField;
@Input('secondInput')
String secondField;
@Input()
set someSetter(String x) { }
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final inputs = component.inputs;
expect(inputs, hasLength(5));
{
final input = inputs[0];
expect(input.name, 'leadingText');
expect(input.nameOffset, code.indexOf("leadingText',"));
expect(input.setterRange.offset, input.nameOffset);
expect(input.setterRange.length, 'leadingText'.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'leadingText');
expect(input.setterType.toString(), equals("String"));
}
{
final input = inputs[1];
expect(input.name, 'tailText');
expect(input.nameOffset, code.indexOf("tailText']"));
expect(input.setterRange.offset, code.indexOf("trailingText: "));
expect(input.setterRange.length, 'trailingText'.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'trailingText');
expect(input.setterType.toString(), equals("int"));
}
{
final input = inputs[2];
expect(input.name, 'firstField');
expect(input.nameOffset, code.indexOf('firstField'));
expect(input.nameLength, 'firstField'.length);
expect(input.setterRange.offset, input.nameOffset);
expect(input.setterRange.length, input.name.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'firstField');
expect(input.setterType.toString(), equals("bool"));
}
{
final input = inputs[3];
expect(input.name, 'secondInput');
expect(input.nameOffset, code.indexOf('secondInput'));
expect(input.setterRange.offset, code.indexOf('secondField'));
expect(input.setterRange.length, 'secondField'.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'secondField');
expect(input.setterType.toString(), equals("String"));
}
{
final input = inputs[4];
expect(input.name, 'someSetter');
expect(input.nameOffset, code.indexOf('someSetter'));
expect(input.setterRange.offset, input.nameOffset);
expect(input.setterRange.length, input.name.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'someSetter');
expect(input.setterType.toString(), equals("String"));
}
// assert no syntax errors, etc
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_inputs_deprecatedProperties() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>',
properties: const ['leadingText', 'trailingText: tailText'])
class MyComponent {
String leadingText;
String trailingText;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final inputs = component.inputs;
expect(inputs, hasLength(2));
{
final input = inputs[0];
expect(input.name, 'leadingText');
expect(input.nameOffset, code.indexOf("leadingText',"));
expect(input.setterRange.offset, input.nameOffset);
expect(input.setterRange.length, 'leadingText'.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'leadingText');
}
{
final input = inputs[1];
expect(input.name, 'tailText');
expect(input.nameOffset, code.indexOf("tailText']"));
expect(input.setterRange.offset, code.indexOf("trailingText: "));
expect(input.setterRange.length, 'trailingText'.length);
expect(input.setter, isNotNull);
expect(input.setter.isSetter, isTrue);
expect(input.setter.displayName, 'trailingText');
}
}
// ignore: non_constant_identifier_names
Future test_outputs() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>',
outputs: const ['outputOne', 'secondOutput: outputTwo'])
class MyComponent {
EventEmitter<MyComponent> outputOne;
EventEmitter<String> secondOutput;
@Output()
EventEmitter<int> outputThree;
@Output('outputFour')
EventEmitter fourthOutput;
@Output()
EventEmitter get someGetter => null;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(5));
{
final output = compOutputs[0];
expect(output.name, 'outputOne');
expect(output.nameOffset, code.indexOf("outputOne"));
expect(output.getterRange.offset, output.nameOffset);
expect(output.getterRange.length, 'outputOne'.length);
expect(output.getter, isNotNull);
expect(output.getter.isGetter, isTrue);
expect(output.getter.displayName, 'outputOne');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("MyComponent"));
}
{
final output = compOutputs[1];
expect(output.name, 'outputTwo');
expect(output.nameOffset, code.indexOf("outputTwo']"));
expect(output.getterRange.offset, code.indexOf("secondOutput: "));
expect(output.getterRange.length, 'secondOutput'.length);
expect(output.getter, isNotNull);
expect(output.getter.isGetter, isTrue);
expect(output.getter.displayName, 'secondOutput');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("String"));
}
{
final output = compOutputs[2];
expect(output.name, 'outputThree');
expect(output.nameOffset, code.indexOf('outputThree'));
expect(output.nameLength, 'outputThree'.length);
expect(output.getterRange.offset, output.nameOffset);
expect(output.getterRange.length, output.nameLength);
expect(output.getter, isNotNull);
expect(output.getter.isGetter, isTrue);
expect(output.getter.displayName, 'outputThree');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("int"));
}
{
final output = compOutputs[3];
expect(output.name, 'outputFour');
expect(output.nameOffset, code.indexOf('outputFour'));
expect(output.getterRange.offset, code.indexOf('fourthOutput'));
expect(output.getterRange.length, 'fourthOutput'.length);
expect(output.getter, isNotNull);
expect(output.getter.isGetter, isTrue);
expect(output.getter.displayName, 'fourthOutput');
expect(output.eventType, isNotNull);
expect(output.eventType.isDynamic, isTrue);
}
{
final output = compOutputs[4];
expect(output.name, 'someGetter');
expect(output.nameOffset, code.indexOf('someGetter'));
expect(output.getterRange.offset, output.nameOffset);
expect(output.getterRange.length, output.name.length);
expect(output.getter, isNotNull);
expect(output.getter.isGetter, isTrue);
expect(output.getter.displayName, 'someGetter');
expect(output.eventType, isNotNull);
expect(output.eventType.isDynamic, isTrue);
}
// assert no syntax errors, etc
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_outputs_streamIsOk() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'dart:async';
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
Stream<int> myOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(1));
{
final output = compOutputs[0];
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("int"));
}
}
// ignore: non_constant_identifier_names
Future test_outputs_extendStreamIsOk() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'dart:async';
abstract class MyStream<T> implements Stream<T> { }
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
MyStream<int> myOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(1));
{
final output = compOutputs[0];
expect(output.eventType, isNotNull);
}
}
// ignore: non_constant_identifier_names
Future test_outputs_extendStreamSpecializedIsOk() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'dart:async';
class MyStream extends Stream<int> { }
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
MyStream myOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(1));
{
final output = compOutputs[0];
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("int"));
}
}
// ignore: non_constant_identifier_names
Future test_outputs_extendStreamUntypedIsOk() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'dart:async';
class MyStream extends Stream { }
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
MyStream myOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(1));
{
final output = compOutputs[0];
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("dynamic"));
}
}
// ignore: non_constant_identifier_names
Future test_outputs_notEventEmitterTypeError() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
int badOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
assertErrorInCodeAtPosition(
AngularWarningCode.OUTPUT_MUST_BE_STREAM, code, "badOutput");
}
// ignore: non_constant_identifier_names
Future test_outputs_extendStreamNotStreamHasDynamicEventType() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent {
@Output()
int badOutput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
final component = directives.single;
final compOutputs = component.outputs;
expect(compOutputs, hasLength(1));
{
final output = compOutputs[0];
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("dynamic"));
}
}
// ignore: non_constant_identifier_names
Future test_parameterizedInputsOutputs() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(
selector: 'my-component',
template: '<p></p>')
class MyComponent<T, A extends String, B extends A> {
@Output() EventEmitter<T> dynamicOutput;
@Input() T dynamicInput;
@Output() EventEmitter<A> stringOutput;
@Input() A stringInput;
@Output() EventEmitter<B> stringOutput2;
@Input() B stringInput2;
@Output() EventEmitter<List<B>> listOutput;
@Input() List<B> listInput;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
final component = directives.single;
final compInputs = component.inputs;
expect(compInputs, hasLength(4));
{
final input = compInputs[0];
expect(input.name, 'dynamicInput');
expect(input.setterType, isNotNull);
expect(input.setterType.toString(), equals("dynamic"));
}
{
final input = compInputs[1];
expect(input.name, 'stringInput');
expect(input.setterType, isNotNull);
expect(input.setterType.toString(), equals("String"));
}
{
final input = compInputs[2];
expect(input.name, 'stringInput2');
expect(input.setterType, isNotNull);
expect(input.setterType.toString(), equals("String"));
}
{
final input = compInputs[3];
expect(input.name, 'listInput');
expect(input.setterType, isNotNull);
expect(input.setterType.toString(), equals("List<String>"));
}
final compOutputs = component.outputs;
expect(compOutputs, hasLength(4));
{
final output = compOutputs[0];
expect(output.name, 'dynamicOutput');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("dynamic"));
}
{
final output = compOutputs[1];
expect(output.name, 'stringOutput');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("String"));
}
{
final output = compOutputs[2];
expect(output.name, 'stringOutput2');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("String"));
}
{
final output = compOutputs[3];
expect(output.name, 'listOutput');
expect(output.eventType, isNotNull);
expect(output.eventType.toString(), equals("List<String>"));
}
// assert no syntax errors, etc
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_finalPropertyInputError() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '<p></p>')
class MyComponent {
@Input() final int immutable = 1;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.INPUT_ANNOTATION_PLACEMENT_INVALID,
code,
"@Input()");
}
// ignore: non_constant_identifier_names
Future test_finalPropertyInputErrorNonDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
class MyNonDirective {
@Input() final int immutable = 1;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.INPUT_ANNOTATION_PLACEMENT_INVALID,
code,
"@Input()");
}
// ignore: non_constant_identifier_names
Future test_finalPropertyInputStringError() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '<p></p>', inputs: const ['immutable'])
class MyComponent {
final int immutable = 1;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate. Can't easily assert position though because its all 'immutable'
errorListener
.assertErrorsWithCodes([StaticTypeWarningCode.UNDEFINED_SETTER]);
}
// ignore: non_constant_identifier_names
Future test_noDirectives() async {
final source = newSource('/test.dart', r'''
class A {}
class B {}
''');
await getDirectives(source);
expect(directives, isEmpty);
}
// ignore: non_constant_identifier_names
Future test_inputOnGetterIsError() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class MyComponent {
@Input()
String get someGetter => null;
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
assertErrorInCodeAtPosition(
AngularWarningCode.INPUT_ANNOTATION_PLACEMENT_INVALID,
code,
"@Input()");
}
// ignore: non_constant_identifier_names
Future test_outputOnSetterIsError() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class MyComponent {
@Output()
set someSetter(x) { }
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
assertErrorInCodeAtPosition(
AngularWarningCode.OUTPUT_ANNOTATION_PLACEMENT_INVALID,
code,
"@Output()");
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp)
ContentChildComp contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.first;
final childFields = component.contentChildFields;
expect(childFields, hasLength(1));
final child = childFields.first;
expect(child.fieldName, equals("contentChild"));
expect(child.nameRange.offset, equals(code.indexOf("ContentChildComp)")));
expect(child.nameRange.length, equals("ContentChildComp".length));
expect(child.typeRange.offset, equals(code.indexOf("ContentChildComp ")));
expect(child.typeRange.length, equals("ContentChildComp".length));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildrenDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren(ContentChildComp)
QueryList<ContentChildComp> contentChildren;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.first;
final childrenFields = component.contentChildrenFields;
expect(childrenFields, hasLength(1));
final children = childrenFields.first;
expect(children.fieldName, equals("contentChildren"));
expect(
children.nameRange.offset, equals(code.indexOf("ContentChildComp)")));
expect(children.nameRange.length, equals("ContentChildComp".length));
expect(children.typeRange.offset,
equals(code.indexOf("QueryList<ContentChildComp>")));
expect(children.typeRange.length,
equals("QueryList<ContentChildComp>".length));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildChildrenNoRangeNotRecorded() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren()
QueryList<ContentChildComp> contentChildren;
@ContentChild()
ContentChildComp contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.first;
final childrenFields = component.contentChildrenFields;
expect(childrenFields, hasLength(0));
final childFields = component.contentChildFields;
expect(childFields, hasLength(0));
// validate
errorListener.assertErrorsWithCodes([
CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS
]);
}
// ignore: non_constant_identifier_names
Future test_hasContentChildChildrenSetter() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp) // 1
void set contentChild(ContentChildComp contentChild) => null;
@ContentChildren(ContentChildComp) // 2
void set contentChildren(QueryList<ContentChildComp> contentChildren) => null;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final component = directives.first;
final childFields = component.contentChildFields;
expect(childFields, hasLength(1));
final child = childFields.first;
expect(child.fieldName, equals("contentChild"));
expect(
child.nameRange.offset, equals(code.indexOf("ContentChildComp) // 1")));
expect(child.nameRange.length, equals("ContentChildComp".length));
expect(child.typeRange.offset, equals(code.indexOf("ContentChildComp ")));
expect(child.typeRange.length, equals("ContentChildComp".length));
final childrenFields = component.contentChildrenFields;
expect(childrenFields, hasLength(1));
final children = childrenFields.first;
expect(children.fieldName, equals("contentChildren"));
expect(children.nameRange.offset,
equals(code.indexOf("ContentChildComp) // 2")));
expect(children.nameRange.length, equals("ContentChildComp".length));
expect(children.typeRange.offset,
equals(code.indexOf("QueryList<ContentChildComp>")));
expect(children.typeRange.length,
equals("QueryList<ContentChildComp>".length));
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasExports() async {
final code = r'''
import 'package:angular2/angular2.dart';
const foo = null;
void bar() {}
class MyClass {}
@Component(selector: 'my-component', template: '',
exports: const [foo, bar, MyClass])
class ComponentA {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final Component component = directives.first;
expect(component.view, isNotNull);
expect(component.view.exports, hasLength(3));
{
final export = component.view.exports[0];
expect(export.identifier, equals('foo'));
expect(export.prefix, equals(''));
expect(export.span.offset, equals(code.indexOf('foo,')));
expect(export.span.length, equals('foo'.length));
expect(export.element, isNull); // not yet linked
}
{
final export = component.view.exports[1];
expect(export.identifier, equals('bar'));
expect(export.prefix, equals(''));
expect(export.span.offset, equals(code.indexOf('bar,')));
expect(export.span.length, equals('bar'.length));
expect(export.element, isNull); // not yet linked
}
{
final export = component.view.exports[2];
expect(export.identifier, equals('MyClass'));
expect(export.prefix, equals(''));
expect(export.span.offset, equals(code.indexOf('MyClass]')));
expect(export.span.length, equals('MyClass'.length));
expect(export.element, isNull); // not yet linked
}
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_prefixedExport() async {
newSource('/prefixed.dart', 'const foo = null;');
final code = r'''
import 'package:angular2/angular2.dart';
import '/prefixed.dart' as prefixed;
const foo = null;
@Component(selector: 'my-component', template: '',
exports: const [prefixed.foo, foo])
class ComponentA {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
final Component component = directives.first;
expect(component.view, isNotNull);
expect(component.view.exports, hasLength(2));
{
final export = component.view.exports[0];
expect(export.identifier, equals('foo'));
expect(export.prefix, equals('prefixed'));
expect(export.span.offset, equals(code.indexOf('prefixed.foo')));
expect(export.span.length, equals('prefixed.foo'.length));
expect(export.element, isNull); // not yet linked
}
{
final export = component.view.exports[1];
expect(export.identifier, equals('foo'));
expect(export.prefix, equals(''));
expect(export.span.offset, equals(code.indexOf('foo]')));
expect(export.span.length, equals('foo'.length));
expect(export.element, isNull); // not yet linked
}
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasNonIdentifierExport() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '', exports: const [1])
class ComponentA {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.EXPORTS_MUST_BE_PLAIN_IDENTIFIERS, code, '1');
}
// ignore: non_constant_identifier_names
Future test_hasRepeatedExports() async {
final code = r'''
import 'package:angular2/angular2.dart';
const foo = null;
@Component(selector: 'my-component', template: '', exports: const [foo, foo])
class ComponentA {
}
''';
final source = newSource('/test.dart', code);
await getDirectives(source);
// validate. Can't validate position because foo occurs so many times
errorListener.assertErrorsWithCodes([AngularWarningCode.DUPLICATE_EXPORT]);
}
}
@reflectiveTest
class BuildUnitViewsTest extends AbstractAngularTest {
List<AbstractDirective> directives;
List<Pipe> pipes;
List<View> views;
List<AnalysisError> errors;
Future getViews(final source) async {
final dartResult = await dartDriver.getResult(source.fullName);
fillErrorListener(dartResult.errors);
final result = await angularDriver.getAngularTopLevels(source.fullName);
directives = result.directives;
pipes = result.pipes;
final linker = new ChildDirectiveLinker(
angularDriver,
angularDriver,
await angularDriver.getStandardAngular(),
new ErrorReporter(errorListener, source));
await linker.linkDirectivesAndPipes(
directives, pipes, dartResult.unit.element.library);
views = directives
.map((d) => d is Component ? d.view : null)
.where((d) => d != null)
.toList();
errors = result.errors;
fillErrorListener(errors);
}
// ignore: non_constant_identifier_names
Future test_buildViewsDoesntGetDependentDirectives() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'other_file.dart';
@Component(selector: 'my-component', template: 'My template',
directives: const [OtherComponent])
class MyComponent {}
''';
final otherCode = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'other-component', template: 'My template',
directives: const [NgFor])
class OtherComponent {}
''';
final source = newSource('/test.dart', code);
newSource('/other_file.dart', otherCode);
await getViews(source);
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(1));
}
// shouldn't be run yet
for (final directive in view.directives) {
if (directive is Component) {
expect(directive.view.directives, hasLength(0));
}
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_directives() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
class DirectiveA {}
@Directive(selector: '[bbb]')
class DirectiveB {}
@Directive(selector: '[ccc]')
class DirectiveC {}
const DIR_AB = const [DirectiveA, DirectiveB];
@Component(selector: 'my-component', template: 'My template',
directives: const [DIR_AB, DirectiveC])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(3));
final directiveClassNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveClassNames,
unorderedEquals(['DirectiveA', 'DirectiveB', 'DirectiveC']));
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_directives_not_list_syntax() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
class DirectiveA {}
@Directive(selector: '[bbb]')
class DirectiveB {}
const VARIABLE = const [DirectiveA, DirectiveB];
@Component(selector: 'my-component', template: 'My template',
directives: VARIABLE)
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final view = getViewByClassName(views, 'MyComponent');
expect(
view.directivesStrategy, const isInstanceOf<UseConstValueStrategy>());
final directiveClassNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveClassNames, unorderedEquals(['DirectiveA', 'DirectiveB']));
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_directives_not_list_syntax_errorWithinVariable() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: 'My template',
directives: VARIABLE)
class MyComponent {}
// A non-array is a type error in the analyzer; a non-component in an array is
// not so we must test it. Define below usage for asserting position.
const VARIABLE = const [Object];
''';
final source = newSource('/test.dart', code);
await getViews(source);
final view = getViewByClassName(views, 'MyComponent');
expect(
view.directivesStrategy, const isInstanceOf<UseConstValueStrategy>());
assertErrorInCodeAtPosition(
AngularWarningCode.TYPE_IS_NOT_A_DIRECTIVE, code, 'VARIABLE');
}
// ignore: non_constant_identifier_names
Future test_prefixedDirectives() async {
final otherCode = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
class DirectiveA {}
@Directive(selector: '[bbb]')
class DirectiveB {}
@Directive(selector: '[ccc]')
class DirectiveC {}
const DIR_AB = const [DirectiveA, DirectiveB];
''';
final code = r'''
import 'package:angular2/angular2.dart';
import 'other.dart' as other;
@Component(selector: 'my-component', template: 'My template',
directives: const [other.DIR_AB, other.DirectiveC])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
newSource('/other.dart', otherCode);
await getViews(source);
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(3));
final directiveClassNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveClassNames,
unorderedEquals(['DirectiveA', 'DirectiveB', 'DirectiveC']));
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_recursiveDirectivesList() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
class DirectiveA {}
@Directive(selector: '[bbb]')
class DirectiveB {}
const DIR_AB_DEEP = const [ const [ const [DirectiveA, DirectiveB]]];
@Component(selector: 'my-component', template: 'My template',
directives: const [DIR_AB_DEEP])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(2));
final directiveClassNames =
view.directives.map((directive) => directive.name).toList();
expect(
directiveClassNames, unorderedEquals(['DirectiveA', 'DirectiveB']));
}
}
// no errors
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_validFunctionalDirectivesList() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
void directiveA() {}
@Directive(selector: '[bbb]')
void directiveB() {}
const DIR_AB_DEEP = const [ const [ const [directiveA, directiveB]]];
@Component(selector: 'my-component', template: 'My template',
directives: const [DIR_AB_DEEP])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
errorListener.assertNoErrors();
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(2));
final directiveNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveNames, unorderedEquals(['directiveA', 'directiveB']));
}
}
}
// ignore: non_constant_identifier_names
Future test_directivesList_invalidDirectiveEntries() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: '[aaa]')
class DirectiveA {}
@Directive(selector: '[bbb]')
void directiveB() {}
void notADirective() {}
class NotADirectiveEither {}
const DIR_AB_DEEP = const [ const [ const [
DirectiveA, directiveB, notADirective, NotADirectiveEither]]];
@Component(selector: 'my-component', template: 'My template',
directives: const [DIR_AB_DEEP])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
{
final view = getViewByClassName(views, 'MyComponent');
{
expect(view.directives, hasLength(2));
final directiveNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveNames, unorderedEquals(['DirectiveA', 'directiveB']));
}
}
errorListener.assertErrorsWithCodes([
AngularWarningCode.TYPE_IS_NOT_A_DIRECTIVE,
AngularWarningCode.FUNCTION_IS_NOT_A_DIRECTIVE
]);
}
// ignore: non_constant_identifier_names
Future test_directives_hasError_notListVariable() async {
final code = r'''
import 'package:angular2/angular2.dart';
const NOT_DIRECTIVE_LIST = 42;
@Component(selector: 'my-component', template: 'My template',
directives: const [NOT_DIRECTIVE_LIST])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularWarningCode.TYPE_IS_NOT_A_DIRECTIVE]);
}
// ignore: non_constant_identifier_names
Future test_hasError_StringValueExpected() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', template: 55)
class ComponentA {
}
''');
await getViews(source);
errorListener.assertErrorsWithCodes(<ErrorCode>[
AngularWarningCode.STRING_VALUE_EXPECTED,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
]);
}
// ignore: non_constant_identifier_names
Future test_constantExpressionTemplateOk() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', template: 'abc' + 'bcd')
class ComponentA {
}
''');
await getViews(source);
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_constantExpressionTemplateComplexIsOnlyError() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
const String tooComplex = 'bcd';
@Component(selector: 'aaa', template: 'abc' + tooComplex + "{{invalid {{stuff")
class ComponentA {
}
''');
await getViews(source);
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularHintCode.OFFSETS_CANNOT_BE_CREATED]);
}
// ignore: non_constant_identifier_names
Future test_hasError_TypeLiteralExpected() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', template: 'AAA', directives: const [42])
class ComponentA {
}
''');
await getViews(source);
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularWarningCode.TYPE_LITERAL_EXPECTED]);
}
// ignore: non_constant_identifier_names
Future test_hasError_TemplateAndTemplateUrlDefined() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa', template: 'AAA', templateUrl: 'a.html')
class ComponentA {
}
''');
newSource('/a.html', '');
await getViews(source);
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularWarningCode.TEMPLATE_URL_AND_TEMPLATE_DEFINED]);
}
// ignore: non_constant_identifier_names
Future test_hasError_NeitherTemplateNorTemplateUrlDefined() async {
final source = newSource('/test.dart', r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'aaa')
class ComponentA {
}
''');
await getViews(source);
errorListener.assertErrorsWithCodes(
<ErrorCode>[AngularWarningCode.NO_TEMPLATE_URL_OR_TEMPLATE_DEFINED]);
}
// ignore: non_constant_identifier_names
Future test_hasError_missingHtmlFile() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', templateUrl: 'missing-template.html')
class MyComponent {}
''';
final dartSource = newSource('/test.dart', code);
await getViews(dartSource);
assertErrorInCodeAtPosition(
AngularWarningCode.REFERENCED_HTML_FILE_DOESNT_EXIST,
code,
"'missing-template.html'");
}
// ignore: non_constant_identifier_names
Future test_templateExternal() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', templateUrl: 'my-template.html')
class MyComponent {}
''';
final dartSource = newSource('/test.dart', code);
final htmlSource = newSource('/my-template.html', '');
await getViews(dartSource);
expect(views, hasLength(1));
// MyComponent
final view = getViewByClassName(views, 'MyComponent');
expect(view.component, getComponentByName(directives, 'MyComponent'));
expect(view.templateText, isNull);
expect(view.templateUriSource, isNotNull);
expect(view.templateUriSource, htmlSource);
expect(view.templateSource, htmlSource);
{
final url = "'my-template.html'";
expect(view.templateUrlRange,
new SourceRange(code.indexOf(url), url.length));
}
}
// ignore: non_constant_identifier_names
Future test_templateInline() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'my-directive')
class MyDirective {}
@Component(selector: 'other-component', template: 'Other template')
class OtherComponent {}
@Component(selector: 'my-component', template: 'My template',
directives: const [MyDirective, OtherComponent])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
expect(views, hasLength(2));
{
final view = getViewByClassName(views, 'MyComponent');
expect(view.component, getComponentByName(directives, 'MyComponent'));
expect(view.templateText, ' My template '); // spaces preserve offsets
expect(view.templateOffset, code.indexOf('My template') - 1);
expect(view.templateUriSource, isNull);
expect(view.templateSource, source);
{
expect(view.directives, hasLength(2));
final directiveClassNames =
view.directives.map((directive) => directive.name).toList();
expect(directiveClassNames,
unorderedEquals(['OtherComponent', 'MyDirective']));
}
}
}
// ignore: non_constant_identifier_names
Future test_useFunctionalDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Directive(selector: 'my-directive')
void myDirective() {}
@Component(selector: 'my-component', template: 'My template',
directives: const [myDirective])
class MyComponent {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
errorListener.assertNoErrors();
expect(views, hasLength(1));
final view = views.single;
expect(view.component.name, 'MyComponent');
expect(view.directives, hasLength(1));
final directive = view.directives.single;
expect(directive.name, 'myDirective');
expect(directive, const isInstanceOf<FunctionalDirective>());
}
// ignore: non_constant_identifier_names
Future test_useFunctionNotFunctionalDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: 'My template',
directives: const [notDirective])
class MyComponent {}
// put this after component, so indexOf works in assertErrorInCodeAtPosition
void notDirective() {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
assertErrorInCodeAtPosition(
AngularWarningCode.FUNCTION_IS_NOT_A_DIRECTIVE, code, 'notDirective');
}
// ignore: non_constant_identifier_names
Future test_hasContentChildComponent() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp)
ContentChildComp contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(1));
expect(childs.first.query, const isInstanceOf<DirectiveQueriedChildType>());
final DirectiveQueriedChildType child = childs.first.query;
expect(child.directive, equals(directives[1]));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildrenDirective() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren(ContentChildComp)
QueryList<ContentChildComp> contentChildren;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childrens = component.contentChildren;
expect(childrens, hasLength(1));
expect(
childrens.first.query, const isInstanceOf<DirectiveQueriedChildType>());
final DirectiveQueriedChildType children = childrens.first.query;
expect(children.directive, equals(directives[1]));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildChildrenSetter() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp) // 1
void set contentChild(ContentChildComp contentChild) => null;
@ContentChildren(ContentChildComp) // 2
void set contentChildren(QueryList<ContentChildComp> contentChildren) => null;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childrens = component.contentChildren;
expect(childrens, hasLength(1));
expect(
childrens.first.query, const isInstanceOf<DirectiveQueriedChildType>());
final DirectiveQueriedChildType children = childrens.first.query;
expect(children.directive, equals(directives[1]));
final childs = component.contentChilds;
expect(childs, hasLength(1));
expect(childs.first.query, const isInstanceOf<DirectiveQueriedChildType>());
final DirectiveQueriedChildType child = childs.first.query;
expect(child.directive, equals(directives[1]));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildLetBound() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild('foo')
ContentChildComp contentChildDirective;
@ContentChild('fooTpl')
TemplateRef contentChildTpl;
@ContentChild('fooElem')
ElementRef contentChildElem;
@ContentChild('fooDynamic')
dynamic contentChildDynamic;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(4));
final LetBoundQueriedChildType childDirective = childs
.singleWhere((c) => c.field.fieldName == "contentChildDirective")
.query;
expect(childDirective, const isInstanceOf<LetBoundQueriedChildType>());
expect(childDirective.letBoundName, equals("foo"));
expect(childDirective.containerType.toString(), equals("ContentChildComp"));
final LetBoundQueriedChildType childTemplate =
childs.singleWhere((c) => c.field.fieldName == "contentChildTpl").query;
expect(childTemplate, const isInstanceOf<LetBoundQueriedChildType>());
expect(childTemplate.letBoundName, equals("fooTpl"));
expect(childTemplate.containerType.toString(), equals("TemplateRef"));
final LetBoundQueriedChildType childElement = childs
.singleWhere((c) => c.field.fieldName == "contentChildElem")
.query;
expect(childElement, const isInstanceOf<LetBoundQueriedChildType>());
expect(childElement.letBoundName, equals("fooElem"));
expect(childElement.containerType.toString(), equals("ElementRef"));
final LetBoundQueriedChildType childDynamic = childs
.singleWhere((c) => c.field.fieldName == "contentChildDynamic")
.query;
expect(childDynamic, const isInstanceOf<LetBoundQueriedChildType>());
expect(childDynamic.letBoundName, equals("fooDynamic"));
expect(childDynamic.containerType.toString(), equals("dynamic"));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildrenLetBound() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren('foo')
QueryList<ContentChildComp> contentChildDirective;
@ContentChildren('fooTpl')
QueryList<TemplateRef> contentChildTpl;
@ContentChildren('fooElem')
QueryList<ElementRef> contentChildElem;
@ContentChildren('fooDynamic')
QueryList contentChildDynamic;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childrens = component.contentChildren;
expect(childrens, hasLength(4));
final LetBoundQueriedChildType childrenDirective = childrens
.singleWhere((c) => c.field.fieldName == "contentChildDirective")
.query;
expect(childrenDirective, const isInstanceOf<LetBoundQueriedChildType>());
expect(childrenDirective.letBoundName, equals("foo"));
expect(
childrenDirective.containerType.toString(), equals("ContentChildComp"));
final LetBoundQueriedChildType childrenTemplate = childrens
.singleWhere((c) => c.field.fieldName == "contentChildTpl")
.query;
expect(childrenTemplate, const isInstanceOf<LetBoundQueriedChildType>());
expect(childrenTemplate.letBoundName, equals("fooTpl"));
expect(childrenTemplate.containerType.toString(), equals("TemplateRef"));
final LetBoundQueriedChildType childrenElement = childrens
.singleWhere((c) => c.field.fieldName == "contentChildElem")
.query;
expect(childrenElement, const isInstanceOf<LetBoundQueriedChildType>());
expect(childrenElement.letBoundName, equals("fooElem"));
expect(childrenElement.containerType.toString(), equals("ElementRef"));
final LetBoundQueriedChildType childrenDynamic = childrens
.singleWhere((c) => c.field.fieldName == "contentChildDynamic")
.query;
expect(childrenDynamic, const isInstanceOf<LetBoundQueriedChildType>());
expect(childrenDynamic.letBoundName, equals("fooDynamic"));
expect(childrenDynamic.containerType.toString(), equals("dynamic"));
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildElementRef() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ElementRef)
ElementRef contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(1));
expect(
childs.first.query, const isInstanceOf<ElementRefQueriedChildType>());
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildrenElementRef() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren(ElementRef)
QueryList<ElementRef> contentChildren;
}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childrens = component.contentChildren;
expect(childrens, hasLength(1));
expect(childrens.first.query,
const isInstanceOf<ElementRefQueriedChildType>());
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildrenTemplateRef() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChildren(TemplateRef)
QueryList<TemplateRef> contentChildren;
}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childrens = component.contentChildren;
expect(childrens, hasLength(1));
expect(childrens.first.query,
const isInstanceOf<TemplateRefQueriedChildType>());
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildTemplateRef() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(TemplateRef)
TemplateRef contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(1));
expect(
childs.first.query, const isInstanceOf<TemplateRefQueriedChildType>());
// validate
errorListener.assertNoErrors();
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective_notRecognizedType() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(String)
ElementRef contentChild;
}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(0));
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.UNKNOWN_CHILD_QUERY_TYPE, code, 'String');
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective_htmlNotAllowed() async {
final code = r'''
import 'package:angular2/angular2.dart';
import 'dart:html';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(AnchorElement)
AnchorElement contentChild;
}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(0));
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.UNKNOWN_CHILD_QUERY_TYPE, code, 'AnchorElement');
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective_notTypeOrString() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(const [])
ElementRef contentChild;
}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(0));
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.UNKNOWN_CHILD_QUERY_TYPE, code, 'const []');
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective_notAssignable() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp)
String contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);
await getViews(source);
final component = directives.first;
final childs = component.contentChilds;
expect(childs, hasLength(1));
expect(childs.first.query, const isInstanceOf<DirectiveQueriedChildType>());
final DirectiveQueriedChildType child = childs.first.query;
expect(child.directive, equals(directives[1]));
// validate
assertErrorInCodeAtPosition(
AngularWarningCode.INVALID_TYPE_FOR_CHILD_QUERY, code, 'String');
}
// ignore: non_constant_identifier_names
Future test_hasContentChildDirective_dynamicOk() async {
final code = r'''
import 'package:angular2/angular2.dart';
@Component(selector: 'my-component', template: '')
class ComponentA {
@ContentChild(ContentChildComp)
dynamic contentChild;
}
@Component(selector: 'foo', template: '')
class ContentChildComp {}
''';
final source = newSource('/test.dart', code);