Access the DOM in Dart unit tests - dart

I am trying to write a test for the Pirate Badge tutorial thats meant to get people started using Dart.
I have the following directory structure:
The code in 6-piratebadge is an untouched version of what comes in the tutorial.
What I have added is the test package.
test/test.html contains:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script type="application/dart" src="test.dart"></script>
<script type="text/javascript" src="packages/unittest/test_controller.js"></script>
</body>
</html>
and test/test.dart contains:
import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'dart:html';
import '../6-piratebadge/piratebadge.dart' as app;
void main() {
useHtmlConfiguration();
app.main();
var button = querySelector("#generateButton");
test('button text', (){
expect(button.text, equals('Aye! Gimme a name!'));
});
}
I run the test like this, from the directory above web:
Content\ Shell web/test/test.html
And I get "Error: Script error." (I am deliberately not using --dump-render-tree so I can inspect the output in the console of the launched browser). The essence of it seems to be this message:
5500:1287:0827/110337:24546692395575:INFO:CONSOLE(0)] "Exception: The null object does not have a getter 'onInput'.
NoSuchMethodError: method not found: 'onInput'
Receiver: null
Arguments: []", source: file:///Users/mikehogan/projects/learning/one-hour-codelab-master/web/6-piratebadge/piratebadge.dart (0)
This is occurring on in these lines of the piratebadge.dart:
void main() {
InputElement inputField = querySelector('#inputName');
inputField.onInput.listen(updateBadge);
So I guess the input with ID "inputName" is not being found.
Its is in piratebadge.html though:
<div>
<input type="text" id="inputName" maxlength="15" disabled>
</div>
Any idea what I am doing wrong?

piratebadge.html is only a file in the same package but not related to your unit test in any way.
'#inputName' wasn't found because the test.html file doesn't contain it.
You can try attempts discussed in this question DART - exception in unit testing

Related

Using custom html when running unit test

I am trying to use a custom HTML page when running a unit test. I am following the instructions outlined on https://github.com/dart-lang/test#running-tests-with-custom-html
My test code (in file test/custom_html_test.dart) looks like
#TestOn( 'browser')
import 'dart:html';
import 'package:test/test.dart';
main( ){
test( "find select", (){
SelectElement selectAddTimezone = querySelector('#addNewTimezone');
expect( selectAddTimezone, isNotNull);
});
}
and the html page (in file test/custom_html_test.html) looks like
<!doctype html>
<!-- custom_html_test.html -->
<html>
<head>
<title>Custom HTML Test</title>
<link rel="x-dart-test" href="custom_html_test.dart">
<script src="packages/test/dart.js"></script>
</head>
<body>
<select id="selectAddTimezone"></select>
</body>
</html>
I use the following to run the test
pub run test -p dartium test\custom_html_test.dart
Dartium starts up and loads the page 'test Browser Host' to run the test - which fails due to the code not finding the selectAddTimezone element, not the custom html I provided.
I am sure I am missing something simple....
Richard
Just use the correct selector
SelectElement selectAddTimezone = querySelector('#addNewTimezone');
should be
SelectElement selectAddTimezone = querySelector('#selectAddTimezone');

The built-in library 'dart:html' is not available on the stand-alone VM

I'd like to be able to unit test my custom polymer elements.
Given a component in lib/web_components:
class Foo extends PolymerElement {
Foo.created() : super.created();
}
and a test in test/web_components:
main() {
test("Should Be a PolymerElement", (){
Foo undertest = new Foo.created();
expect(undertest, new isInstanceOf<PolymerElement>());
});
}
Running the test results in the error mentioned in the title. How can I avoid this error?
Edit:
So I've tried adding #TestOn('content-shell') at the top of my client side test files, and adding #TestOn('vm') to the server side tests.
In Grinder I have:
#Task('Test')
test() {
new PubApp.local('test').run([]);
new PubApp.local('test').run(["-p", "content-shell"]);
}
The server-side tests run fine but the client-side tests throw the following error:
pub run test -p content-shell test/web_component/foo_test.dart
"Failed to start content shell: No such file or directory"
Edit 2:
I've tried running in the dartium platform with the following command since content-shell doesn't seem to work:
pub run test:test -p dartium test/web_component/foo_test.dart
The result is:
Failed to load "test/web_component/foo_test.dart": type 'test.backend.declarer.Declarer' is not a subtype of type 'test.backend.declarer.Declarer' of 'function result'.
packages/test/test.dart 44:32 _declarer
packages/test/test.dart 108:5 test
foo_test.dart 9:3 main
package:test IframeListener.start
foo_test.dart.browser_test.dart 6:18 main
You need to create an html page for the test and run the test in the browser. The new test package has a decent readme explaining how to do that. (I have an issue that browser tests time out often. This is a known issue and will probably be fixed soon.)
Update
See Instantiating polymer element via dart code, Dynamically create polymer element for how to create a Polymer element dynamically.
Instead of main() use
#whenPolymerReady
init() {
#TestOn('content-shell') is ok but it's better to use #TestOn('browser') and then specify the concrete browser using the -p argument (-pchrome, -pdartium, -pfirefox, -pcontent-shell, ... see test README for a list of supported browsers). You can pass more than one -p at a time to run the tests at more than one browser.
You can also use a custom HTML page for the test
<!doctype html>
<!-- custom_html_test.html -->
<html>
<head>
<title>browser/polymer test</title>
<link rel="import" href="app_element.html">
<link rel="import" href="child_element.html">
<link rel="x-dart-test" href="html_test.dart">
<script src="packages/test/dart.js"></script>
</head>
<body>
<div id="granny">
<div>
<div id="parent">
<div>
<div id="child">child</div>
</div>
</div>
</div>
</div>
<app-element id="polymer-parent" unresolved>
<child-element id="polymer-child"></child-element>
</app-element>
</body>
</html>
where the file names needs to be the same as the Dart test file except .html as file extension and <link rel="x-dart-test" href="html_test.dart"> refers to your Dart test file. app-element, child-element are Polymer elements I used in my test (like your Foo)
Another update
I assume you need to use a custom HTML file (not tried without yet) for Polymer tests because otherwise there is no way to register an entry point for the transformer.
And then add the html file as entry point to the Polymer transformer section in pubspec.yaml

Simple Dart Web Component Not Working

Short Version: The custom web component example in the first link isn't working for me. Why not? I'm running this in Dartium.
Long Version:
I copied and pasted this Dart Web UI example from this tutorial into the Dart Editor and tried to run it. Nothing showed up on the page. I've used dart before but not with web components, so I noticed that one difference between this code and other code I've written is that there was no <script src="packages/browser/dart.js"></script>, so I added that at the bottom. Now I got the error:
Internal error: Dart_Invoke: did not find top-level function 'main'.
I put a print statement in the main() function, and the text was printed before the error, which is strange. I guessed and tried adding main() {} inside the <script> tag that was in the custom component. That is, the script tag looked like:
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
main() {print("in main in component");}
class CounterComponent extends WebComponent {
int count = 0;
void increment() { count++; }
}
</script>
Now that error goes away, and both print statements are printed, but nothing happens.
Here is the original tutorial code for your convenience:
<!DOCTYPE html>
<!--
Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.css">
</head>
<body>
<element name="click-counter" constructor="CounterComponent" extends="div">
<template>
<button on-click="increment()">Click me</button>
<span>(click count: {{count}})</span>
</template>
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
class CounterComponent extends WebComponent {
int count = 0;
void increment() { count++; }
}
</script>
</element>
<div class="well">
<div is="click-counter"></div>
</div>
<script type="application/dart">
main(){}
</script>
</body>
</html>
Web UI applications need to be "built" (usually by a script called build.dart in the project root, see this article for more details).
If I go to the Dart Editor, create a new test project using the wizard and selecting the Web Project (using the web_ui library) option, this creates the boilerplate including the build script.
Open up the html file and paste in the code from the github tutorial, replacing what is there already. When you save the file, the build.dart will be invoked, outputting the build version to /web/out/
Hit the run button, and the Dart Editor will open the app in Dartium (it knows to add /out/ to the URL).

dart clipboardData always null

The following
main.html
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="test" draggable="true">hello world</p>
<script type="application/dart" src="main.dart"></script>
<!-- for this next line to work, your pubspec.yaml file must have a dependency on 'browser' -->
<script src="packages/browser/dart.js"></script>
</body>
</html>
and main.dart
import 'dart:html';
void main() {
var elem = query('#test');
elem.onDragStart.listen((evt) {
evt.clipboardData.setData('text/html', elem.innerHtml);
});
}
are producing the exception
The null object does not have a method 'setData'.
NoSuchMethodError : method not found: 'setData'
Receiver: null
Arguments: ["text/html", "hello world"]
I've searched, but can find no relevant information about what I could possibly be doing wrong, or about clipboardData in dart at all (even the API is silent on the issue, and the source dart:html file just points to "native code"
(From my comment to the original question)
Use this instead:
evt.dataTransfer.setData('text/html', elem.innerHtml);
It is an attribute on MouseEvent (api ref) rather than the base Event class, and you can get autocomplete and remove editor warnings by explicitly declaring that evt is of type MouseEvent:
elem.onDragStart.listen((MouseEvent evt) {
evt.dataTransfer.setData('text/html', elem.innerHtml);
});
clipboardData seems to have been deprecated without warning. In web_ui components (where I first observed this), the dataTransfer attribute is not recognised by the editor as a valid attribute on instances of Event. I can't find any reference on the web as to why this change was made (the API docs don't say anything on the matter and I couldn't find any recent posts on the discussion board)...

When is the removed lifecycle method called for Dart web components?

Latest edit:
This is an open issue in web-ui: https://github.com/dart-lang/web-ui/issues/245
Previously:
I'm trying to figure out how to get removed() from the web component lifecycle methods to be called. Looking through the generated code for my below example, I see there's a call to autogenerated.dispatch(); after replaceElement() which I hoped would be what calls removed(), but I don't see my print statement output.
Maybe related: I glanced through the spec trying to understand what the output of build.dart is doing for the lifecycle methods. Perhaps the spec is out of date? I still don't see composeChildren() listed in the instantiation section of the spec (which is mentioned in this web-ui issue comment) even though composeChildren() gets called in the autogenerated code from build.dart.
The reason behind this question is my interest in a Dart webapp able to load and unload web components within a single parent html file programmatically (via the instantiation instructions in the spec), instead of having to declare web components in the html. I'm running with web_ui-0.2.11. Thanks!
webcomponent:
<!DOCTYPE html>
<html><body>
<element name="x-lifecycle-test" constructor="LifecycleTest" extends="div">
<template> {{foo}} </template>
<script type="application/dart">
import 'dart:html';
import 'package:web_ui/web_ui.dart';
var foo = "testing lifecycle methods";
class LifecycleTest extends WebComponent{
inserted() => print("inserted");
removed() => print("removed");
}
</script>
</element>
</body></html>
Parent html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"><title>Lifecycle</title>
<link rel="components" href="lifecycle_test.html">
</head>
<body>
<div>
<button on-click="replaceElement()">replace element</button>
</div>
<div id='holder'>
<x-lifecycle-test></x-lifecycle-test>
</div>
<script type="application/dart">
import 'dart:html';
void replaceElement() {
query('#holder').replaceWith(new DivElement()
..id = 'holder'
..text = 'replaced');
}
main() {}
</script>
<script type='text/javascript' src="https://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script>
</body>
</html>
Adding an answer here for completeness: web components are used in Dart using Polymer. When an instance of a custom element is removed from the DOM, the leftView life cycle method triggers. You can read more about this at https://www.dartlang.org/docs/tutorials/polymer-intro/#life-cycle-methods.

Resources