Skip to content

Commit

Permalink
Merge pull request #36 from DrMarcII/master
Browse files Browse the repository at this point in the history
Add async pageloader.
  • Loading branch information
DrMarcII committed Apr 9, 2015
2 parents 2ac1ed3 + baef7ae commit 8e47a2e
Show file tree
Hide file tree
Showing 33 changed files with 3,067 additions and 39 deletions.
506 changes: 506 additions & 0 deletions dart/lib/async/html.dart

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions dart/lib/async/objects.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2014 Google Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// PageLoader Objects provides the core interface and annotation definitions
/// that should be used within PageLoader Objects.
library pageloader.async.objects;

export 'src/annotations.dart';
export 'src/interfaces.dart';
308 changes: 308 additions & 0 deletions dart/lib/async/src/annotations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// Copyright 2014 Google Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

library pageloader.async.annotations;

import 'dart:async';

import 'interfaces.dart';

// Utility Annotations

/// Normally if an element is not found, an exception is thrown. This makes
/// it difficult to test for the absence of something in the DOM. To allow an
/// element to be absent from the DOM, annotate it with this.
const optional = const _Optional();
class _Optional {
const _Optional();

@override
String toString() => '@optional';
}

const inject = const _Inject();
class _Inject {
const _Inject();

@override
String toString() => '@inject';
}

/// By default PageLoader ensure that all elements are displayed.
/// Adding @disableDisplayedCheck to an annotated field or class will disable this check from this
/// point onwards.
const disableDisplayedCheck = const _DisableDisplayedCheck();
class _DisableDisplayedCheck {
const _DisableDisplayedCheck();

@override
String toString() => '@disableDisplayedCheck';
}

// Finder Annotations

class ById implements Finder {
final String _id;

const ById(this._id);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) =>
context.getElementsByCss('#$_id');

@override
String toString() => '@ById("$_id")';
}

class ByTagName implements Finder {
final String _name;

const ByTagName(this._name);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) =>
context.getElementsByCss(_name);

@override
String toString() => '@ByTagName("$_name")';
}

class ByCss implements Finder {
final String _locator;

const ByCss(this._locator);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) =>
context.getElementsByCss(_locator);

@override
String toString() => '@ByCss("$_locator")';
}

class ByClass implements Finder {
final String _class;

const ByClass(this._class);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) =>
context.getElementsByCss('.$_class');

String toString() => '@ByClass("$_class")';
}

/// Finds elements with the given tag name. Unlike [ByTagName],
/// this will also find the current Root if it is the given tag.
class EnsureTag implements Finder {
final String _name;

const EnsureTag(this._name);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) async* {
if ((await context.name) == this._name) {
yield context;
}
yield* context.getElementsByCss(this._name);
}

@override
String toString() => '@EnsureTag("$_name")';
}

/// Traverses into the shadow dom of the elements found by [of] (or of the
/// current scope if [of] not provided), and then finds elements using [find]
/// if provided.
class InShadowDom implements Finder {
final Finder of;
final Finder find;

const InShadowDom({this.of: root, this.find: root});

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) async* {
await for (var el in of.findElements(context).map((e) => e.shadowRoot)) {
yield* find.findElements(await el);
}
}

@override
String toString() => '@InShadowDom(of: $of, find: $find)';
}

/// Matches the root [PageLoaderElement] being used for constructing the
/// current page object.
const root = const _Root();
class _Root implements Finder {
const _Root();

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) async* {
yield context;
}

@override
String toString() => '@root';
}

/// Return all of the elements found by all of the provided Finders.
/// Note: this does not de-dup elements. The order of the returned
/// elements is based on the order of the finders.
class All implements Finder {
final List<Finder> _finders;

const All(this._finders);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) async* {
for (var finder in _finders) {
yield* finder.findElements(context);
}
}

@override
String toString() => '@All($_finders)';
}

/// Return the elements located by a series of finders and filters running in
/// sequence. For example, @ByChained(finderA, finderB, filterX) will find all
/// elements that match B inside an element that matches A and then filter by X.
/// Note: this does not de-dup elements.
class Chain implements Finder {
final List _annotations;

const Chain(this._annotations);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) {
var elements = () async* {
yield context;
}();

for (var annotation in _annotations) {
if (annotation is Filter) {
elements = annotation.filter(elements);
} else if (annotation is Finder) {
elements = (els) async* {
await for (var el in els) {
yield* annotation.findElements(el);
}
}(elements);
}
}

return elements;
}

@override
String toString() => '@Chain($_annotations)';
}

/// Evaluates the nested annotation from the global context for the PageLoader
/// instance being used.
class Global implements Finder {
final Finder _finder;

const Global([this._finder = root]);

@override
Stream<PageLoaderElement> findElements(PageLoaderElement context) =>
_finder.findElements(context.loader.globalContext);

@override
String toString() => '@Global($_finder)';
}

// Filters

/// Filters element based on visibility.
class IsDisplayed extends ElementFilter {
final bool _displayed;

const IsDisplayed([this._displayed = true]);

@override
Future<bool> keep(PageLoaderElement element) async =>
(await element.displayed) == _displayed;

@override
String toString() => '@IsDisplayed($_displayed)';
}

/// Keeps only [PageLoaderElement]s that have the given attribute with the
/// given value.
class WithAttribute extends ElementFilter {
final String _attribute;
final String _value;

const WithAttribute(this._attribute, this._value);

@override
Future<bool> keep(PageLoaderElement element) async =>
(await element.attributes[_attribute]) == _value;

String toString() => '@WithAttribute($_attribute, $_value)';
}

/// Keeps only [PageLoaderElement]s that correspond to the given tag.
class IsTag extends ElementFilter {
final String _name;

const IsTag(this._name);

@override
Future<bool> keep(PageLoaderElement element) async =>
(await element.name) == _name;

String toString() => '@IsTag("$_name")';
}

/// Keeps only [PageLoaderElement]s with the given class.
class WithClass extends ElementFilter {
final String _class;

const WithClass(this._class);

@override
Future<bool> keep(PageLoaderElement element) async =>
await element.classes.contains(_class);

String toString() => '@WithClass($_class)';
}

/// Keeps only [PageLoaderElement]s with the given inner text.
class WithInnerText extends ElementFilter {
final String _text;

const WithInnerText(this._text);

@override
Future<bool> keep(PageLoaderElement element) async =>
(await element.innerText).contains(_text);

String toString() => '@WithInnerText($_text)';
}

/// Keeps only [PageLoaderElement]s with the given visible text.
class WithVisibleText extends ElementFilter {
final String _text;

const WithVisibleText(this._text);

@override
Future<bool> keep(PageLoaderElement element) async =>
(await element.visibleText).contains(_text);

String toString() => '@WithVisibleText($_text)';
}
Loading

0 comments on commit 8e47a2e

Please sign in to comment.