flagpole
Version:
Simple and fast DOM integration and REST API testing framework.
1,253 lines (1,152 loc) • 37.7 kB
text/typescript
import { Scenario } from "./scenario";
import { iResponse, ResponseType } from "./response";
import { Flagpole } from ".";
import { Link } from "./link";
let $: CheerioStatic = require('cheerio');
export enum NodeType {
Generic,
Element,
StyleAttribute,
Property,
Value
}
/**
* Various different types of properties that assertions can be made against
*/
export class Node {
protected response: iResponse;
protected name: string;
protected obj: any;
protected typeOfNode: NodeType = NodeType.Generic;
protected selector: string | null = null;
constructor(response: iResponse, name: string, obj: any) {
this.response = response;
this.name = name;
this.obj = obj;
}
/**
* Test the raw object to see if its nullish
*/
protected isNullOrUndefined(): boolean {
return Flagpole.isNullOrUndefined(this.obj);
}
/**
* Is this node a DOM Element?
*/
protected isDomElement(): boolean {
return (Flagpole.toType(this.obj) == 'cheerio');
}
public tagName(): Node {
return new Node(this.response, 'Tag of ' + this.name, this.getTagName());
}
protected getTagName(): string | null {
if (this.isDomElement()) {
return this.obj.get(0).tagName;
}
return null;
}
protected getAttribute(name: string): string | null {
if (this.isDomElement()) {
return (typeof this.obj.get(0).attribs[name] !== 'undefined') ?
this.obj.get(0).attribs[name] : null;
}
return null;
}
protected getUrl(): string | null {
if (this.isDomElement()) {
let tagName: string | null = this.getTagName();
if (tagName !== null) {
if (['img', 'script', 'video', 'audio', 'object', 'iframe'].indexOf(tagName) >= 0) {
return this.getAttribute('src');
}
else if (['a', 'link'].indexOf(tagName) >= 0) {
return this.getAttribute('href');
}
else if (['form'].indexOf(tagName) >= 0) {
return this.getAttribute('action') || this.response.scenario.getUrl();
}
}
}
else if (this.isString()) {
if (this.response.getType() == ResponseType.json) {
return this.toString().trim();
}
else if (this.response.getType() == ResponseType.html) {
return this.toString().trim().replace(/^url\(['"]?/, '').replace(/['"]?\)$/, '');
}
}
return null;
}
/**
* Check if the underlying html element is a form tag
*/
protected isFormElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'form';
}
return false;
}
/**
* Check if the underlying html element is a button tag
*/
protected isButtonElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'button';
}
return false;
}
/**
* Check if the underlying html element is an a tag
*/
protected isLinkElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'a' &&
this.getAttribute('href') !== null;
}
return false;
}
protected isImageElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'img' &&
this.getAttribute('src') !== null;
}
return false;
}
protected isScriptElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'script' &&
this.getAttribute('src') !== null;
}
return false;
}
protected isStylesheetElement(): boolean {
if (this.isDomElement()) {
return this.getTagName() === 'link' &&
(this.getAttribute('rel') || '').toLowerCase() == 'stylesheet' &&
this.getAttribute('href') !== null
}
return false;
}
/**
* Is this element one we can fake click on?
*/
protected isClickable(): boolean {
return (this.isLinkElement() || this.isButtonElement());
}
/**
*
*/
protected isArray(): boolean {
return Flagpole.toType(this.obj) == 'array';
}
/**
*
*/
protected isString(): boolean {
return Flagpole.toType(this.obj) == 'string';
}
/**
*
*/
protected isObject(): boolean {
return Flagpole.toType(this.obj) == 'object';
}
/**
*
* @param key
*/
protected hasProperty(key: string): boolean {
return this.obj.hasOwnProperty && this.obj.hasOwnProperty(key);
}
/**
* Write a message for a passing assertion
*
* @param {string} message
*/
protected pass(message: string): Scenario {
return this.response.scenario.pass(message);
}
/**
* Write message for a failing assertion
*
* @param {string} message
*/
protected fail(message: string): Scenario {
return this.response.scenario.fail(message);
}
/**
* Get the raw object
*/
public get(index?: number): any {
if (typeof index !== 'undefined') {
if (this.isArray()) {
return this.obj[index];
}
else if (this.isDomElement()) {
return this.obj.eq(index);
}
}
// Still here? return it all
return this.obj;
}
/**
* Sometimes we need to get the actual string
*/
public toString(): string {
if (this.isDomElement()) {
return (this.obj.text() || this.obj.val()).toString();
}
else if (!this.isNullOrUndefined() && this.obj.toString) {
return this.obj.toString();
}
else {
return String(this.obj);
}
}
/**
* Select another element.
*
* @param path
* @param findIn
*/
public select(path: string, findIn?: any): Node {
let node: Node = this.response.select(path, findIn);
node.typeOfNode = NodeType.Value;
node.selector = path;
return node;
}
/**
*
* @param key
*/
public headers(key?: string): Node {
return this.response.headers(key);
}
/**
*
*/
public status(): Node {
return this.response.status();
}
/**
*
*/
public loadTime(): Node {
return this.response.loadTime();
}
/**
* Gets back to the last element selected
*/
public and(): Node {
return this.response.and();
}
/**
* Flip the next assertion
*/
public not(): Node {
this.response.not();
return this;
}
/**
* Write message for a comment
*
* @param {string} message
*/
public comment(message: string): Node {
this.response.scenario.comment(message);
return this;
}
/**
* Override the default message for this test so we can have a custom message that is more human readable
*
* @param {string} message
*/
public label(message: string): Node {
this.response.label(message);
return this;
}
/**
* For debugging, just spit out a value
*/
public echo(): Node {
this.comment(this.name + ' = ' + this.obj);
return this;
}
/**
* For debugging, just spit out this object's type
*/
public typeof(): Node {
this.comment('typeof ' + this.name + ' = ' + Flagpole.toType(this.obj));
return this;
}
/**
* SIMULATED ACTIONS
*/
/**
* Click on this link (kick off another scenario)
*
* @param {Scenario} nextScenario
*/
public click(scenarioOrTitle: string | Scenario, impliedAssertion: boolean = false): Scenario {
let scenario: Scenario = this.getLambdaScenario(scenarioOrTitle, impliedAssertion);
// If this was a link, click it and then run the resulting scenaior
if (this.isLinkElement()) {
let link: Link = new Link(this.response, this.getAttribute('href') || '').validate();
(link.isNavigation()) ?
scenario.open(link.getUri()) :
scenario.skip('Not a navigation link');
}
// If this was a button and it has a form to submit... submit that form
else if (this.isButtonElement()) {
let formNode: Node = new Node(this.response, 'form', this.obj.parents('form'));
(this.attribute('type').toString().toLowerCase() === 'submit' || !formNode.isFormElement()) ?
formNode.submit(scenario) :
scenario.skip('Button does not submit anything');
}
else {
this.fail('Not a clickable element');
scenario.skip();
}
return scenario;
}
/**
* Simulate form submission
*
* @param nextScenario
*/
public submit(scenarioOrTitle: string | Scenario, impliedAssertion: boolean = false): Scenario {
let scenario: Scenario = this.getLambdaScenario(scenarioOrTitle, impliedAssertion);
let link: Link = new Link(this.response, this.getUrl() || '')
.validate();
if (this.isFormElement() && link.isNavigation()) {
let uri: string;
let method: string = this.getAttribute('method') || 'get';
scenario.method(method);
// Submit form values in query string
if (method == 'get') {
uri = link.getUri(this.obj.serializeArray());
}
// Submit form in multi-part form data
else {
let formDataArray: { name: string, value: string }[] = this.obj.serializeArray();
let formData: any = {};
uri = link.getUri();
formDataArray.forEach(function (input: any) {
formData[input.name] = input.value;
});
scenario.form(formData)
}
scenario.open(uri);
}
else {
scenario.skip('Nothing to submit');
}
return scenario;
}
public fillForm(formData: any): Node {
if (this.isFormElement()) {
this.comment('Filling out form');
if (Flagpole.toType(formData) === 'object') {
let form: Cheerio = this.obj;
for (let name in formData) {
this.assert(
form.find('[name="' + name + '"]').val(formData[name]).val() == formData[name],
'Form field ' + name + ' equals ' + formData[name]
);
}
}
}
else {
this.fail('Not a form');
}
return this;
}
protected getLambdaScenario(scenarioOrTitle: string | Scenario, impliedAssertion: boolean = false): Scenario {
let node: Node = this;
let scenario: Scenario = (function () {
if (typeof scenarioOrTitle == 'string') {
let path: string = node.getUrl() || '';
if (
node.isImageElement() ||
(node.typeOfNode == NodeType.StyleAttribute && node.selector == 'background-image') ||
/\.(jpe?g|gif|png|svg|bmp|webp|tiff?|psd)$/i.test(path)
) {
return node.response.scenario.suite.Image(scenarioOrTitle);
}
else if (
node.isStylesheetElement() ||
/\.css$/i.test(path)
) {
return node.response.scenario.suite.Stylesheet(scenarioOrTitle);
}
else if (
node.isScriptElement() ||
/\.js$/i.test(path)
) {
return node.response.scenario.suite.Script(scenarioOrTitle);
}
else if (
(node.isFormElement() || node.isClickable()) ||
/\.(html?|php|aspx?|jsp)$/i.test(path)
) {
return node.response.scenario.suite.Html(scenarioOrTitle);
}
else {
return node.response.scenario.suite.Resource(scenarioOrTitle);
}
}
return scenarioOrTitle;
})();
if (impliedAssertion) {
scenario.assertions(function () {
// Nothing
});
}
return scenario;
}
public load(scenarioOrTitle: string | Scenario, impliedAssertion: boolean = false): Scenario {
let relativePath: string | null = this.getUrl();
let link: Link = new Link(this.response, relativePath || '').validate();
let scenario: Scenario = this.getLambdaScenario(scenarioOrTitle, impliedAssertion);
if (relativePath === null) {
scenario.skip('No URL to load');
}
else if (link.isNavigation()) {
scenario.open(link.getUri())
}
else {
scenario.skip('Nothing to load');
}
return scenario;
}
/**
* DOM TRAVERSAL
*/
public find(selector: string): Node {
let node: Node = this.response.select(selector, this.obj);
node.selector = selector;
node.typeOfNode = NodeType.Element;
return node;
}
public closest(selector: string): Node {
let name: string = 'closest ' + selector;
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().closest(selector))
);
}
else if (this.isObject()) {
let arrPath: string[] = (this.response.getLastElementPath() || '').split('.');
let found: boolean = false;
// Loop through the path backwards
let i = arrPath.length - 1;
for (; i >= 0; i--) {
if (arrPath[i] == selector) {
found = true;
break;
}
}
// Found something that matched selector.. So build path up to that point
if (found) {
return this.select(arrPath.slice(0, i + 1).join('.'));
}
}
return this.response.setLastElement('', new Node(this.response, name, null));
}
public parents(selector?: string): Node {
let name: string = 'parent ' + selector;
// If there is no selector then this is the same as the parent method
if (typeof selector == 'undefined') {
return this.parent();
}
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().parents(selector))
);
}
else if (this.isObject()) {
let arrPath: string[] = (this.response.getLastElementPath() || '').split('.');
if (arrPath.length > 1) {
// Loop backwards, starting at the second to last element in path
let found: boolean = false;
let i = arrPath.length - 2;
for (; i >= 0; i--) {
if (arrPath[i] == selector) {
found = true;
break;
}
}
// Found something that matched selector.. So build path up to that point
if (found) {
return this.select(arrPath.slice(0, i + 1).join('.'));
}
}
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
public parent(): Node {
let name: string = 'parent';
if (this.isDomElement()) {
return this.response.setLastElement(null, new Node(this.response, name, this.get().parent()));
}
else if (this.isObject()) {
let arrPath: string[] = (this.response.getLastElementPath() || '').split('.');
// If the last selected path is at least 2 deep
if (arrPath.length > 1) {
return this.select(arrPath.slice(0, arrPath.length - 1).join('.'));
}
// Else return top level
else {
return this.response.setLastElement('', new Node(this.response, name, this.response.getRoot()));
}
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
public siblings(selector): Node {
let name: string = 'siblings ' + selector;
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().siblings(selector))
);
}
else if (this.isObject()) {
return this.parent().children(selector);
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
public children(selector): Node {
let name: string = 'children ' + selector;
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().children(selector))
);
}
else if (this.isObject() || this.isArray()) {
let obj: any = this.get();
if (typeof selector !== 'undefined') {
return this.select(selector, obj);
}
return this.response.setLastElement(null, new Node(this.response, name, obj));
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
public next(selector): Node {
let name: string = 'next ' + selector;
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().next(selector))
);
}
else if (this.isObject()) {
return this.parent().children(selector);
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
public prev(selector): Node {
let name: string = 'next ' + selector;
if (this.isDomElement()) {
return this.response.setLastElement(
null, new Node(this.response, name, this.get().prev(selector))
);
}
else if (this.isObject()) {
return this.parent().children(selector);
}
return this.response.setLastElement(null, new Node(this.response, name, null));
}
/**
* Alias for nth because it's what jQuery uses even though it's a stupid name
*
* @param {number} i
*/
public eq(i: number): Node {
return this.nth(i);
}
/**
* Select the nth value or an array or collection
*
* @param {number} i
*/
public nth(i: number): Node {
let obj: any = null;
if (i >= 0) {
if (this.isArray()) {
obj = this.obj[i];
}
else if (this.isDomElement()) {
obj = this.obj.eq(i);
}
}
return this.response.setLastElement(null, new Node(this.response, this.name + '[' + i + ']', obj));
}
/**
* Get the first element in the array
*/
public first(): Node {
return this.nth(0);
}
/**
* Get the last element in the array
*/
public last(): Node {
return this.nth(
(this.obj && this.obj.length) ? (this.obj.length - 1) : -1
);
}
public slice(start: number, end: number): Node {
let name: string = this.name + ' slice(' + start + (end ? ' to ' + end : '') + ')';
if (
this.isDomElement() ||
this.isArray() ||
this.isString()
) {
return new Node(this.response, name, this.get().slice(start, end));
}
return this;
}
/**
* PROPERTIES AND ATTRIBUTES
*/
public css(key: string): Node {
let text: any = null;
if (this.isDomElement()) {
text = this.obj.css(key);
}
let node: Node = new Node(this.response, this.name + '[style][' + key + ']', text);
node.typeOfNode = NodeType.StyleAttribute;
node.selector = key;
return node;
}
/**
* Get the attribute by name of this object
*
* @param {string} key
*/
public attribute(key: string): Node {
let text: any = null;
if (this.isDomElement()) {
text = this.obj.attr(key);
}
else if (!Flagpole.isNullOrUndefined(this.obj) && this.hasProperty(key)) {
text = this.obj[key];
}
else if (this.response.getLastElement().isDomElement()) {
text = this.response.getLastElement().get().attr(key);
}
this.typeOfNode = NodeType.Property;
this.selector = key;
return new Node(this.response, this.name + '[' + key + ']', text);
}
/**
* Get the property by name of this object
*
* @param {string} key
*/
public property(key: string): Node {
let text: any;
if (this.isDomElement()) {
text = this.obj.prop(key);
}
else if (!this.isNullOrUndefined() && this.hasProperty(key)) {
text = this.obj[key];
}
else if (this.response.getLastElement().isDomElement()) {
text = this.response.getLastElement().get().prop(key);
}
this.typeOfNode = NodeType.Property;
this.selector = key;
return new Node(this.response, this.name + '[' + key + ']', text);
}
public prop(key: string): Node {
return this.property(key);
}
/**
* Get the data attribute by name of this object
*
* @param {string} key
*/
public data(key: string): Node {
let text: any = null;
if (this.isDomElement()) {
text = this.obj.data(key);
}
else if (!this.isNullOrUndefined() && this.hasProperty(key)) {
text = this.obj[key];
}
else if (this.response.getLastElement().isDomElement()) {
text = this.response.getLastElement().get().data(key);
}
this.typeOfNode = NodeType.Property;
this.selector = key;
return new Node(this.response, this.name + '[' + key + ']', text);
}
/**
* Get the value of this object
*/
public val(): Node {
let text: any = null;
if (this.isDomElement()) {
text = this.obj.val();
}
else if (!this.isNullOrUndefined()) {
text = this.obj;
}
this.typeOfNode = NodeType.Value;
this.selector = null;
return new Node(this.response, 'Value of ' + this.name, text);
}
/**
* Get the text of this object
*/
public text(): Node {
let text: any = null;
if (this.isDomElement()) {
text = this.obj.text();
}
else if (!this.isNullOrUndefined()) {
text = this.obj.toString();
}
return new Node(this.response, 'Text of ' + this.name, text);
}
/**
* Find the number of elements in array or length of a string
*/
public length(): Node {
let count: number = (this.obj && this.obj.length) ?
this.obj.length : 0;
return new Node(this.response, 'Length of ' + this.name, count);
}
public type(): Node {
return new Node(this.response, 'Type of ' + this.name, Flagpole.toType(this.obj));
}
/**
* Get the float/double value of this object
*/
public parseFloat(): Node {
return new Node(this.response, 'Float of ' + this.name, parseFloat(this.toString()));
}
/**
* Get the integer value of this object
*/
public parseInt(): Node {
return new Node(this.response, 'Integer of ' + this.name, parseInt(this.toString()));
}
/**
* Trim extra whitespace around the string value
*/
public trim(): Node {
let text: string = this.toString().trim();
return new Node(this.response, 'Trimmed text of ' + this.name, text);
}
/**
* Lowercase the string value
*/
public toLowerCase(): Node {
let text: string = this.toString().toLowerCase();
return new Node(this.response, 'Lowercased text of ' + this.name, text);
}
/**
* Uppercase the string value
*/
public toUpperCase(): Node {
let text: string = this.toString().toUpperCase();
return new Node(this.response, 'Uppercased text of ' + this.name, text);
}
/**
* Decodes an encoded string.
*/
public decodeURI(): Node {
let text: string = decodeURI(this.toString());
return new Node(this.response, 'Unescaped text of ' + this.name, text);
}
public decodeURIComponent(): Node {
let text: string = decodeURIComponent(this.toString());
return new Node(this.response, 'Unescaped text of ' + this.name, text);
}
/**
* Encodes a string.
* This function makes a string portable, so it can be transmitted across any network to any computer that supports ASCII characters.
* This function encodes special characters, with the exception of: * @ - _ + . /
*/
public encodeURI(): Node {
let text: string = encodeURI(this.toString());
return new Node(this.response, 'Escaped text of ' + this.name, text);
}
public encodeURIComponent(): Node {
let text: string = encodeURIComponent(this.toString());
return new Node(this.response, 'Escaped text of ' + this.name, text);
}
/**
* Replace the string value
*
* @param {string | RegExp} search
* @param {string} replace
*/
public replace(search: string | RegExp, replace: string): Node {
let text: string = this.toString().replace(search, replace);
return new Node(this.response, 'Replaced text of ' + this.name, text);
}
/**
* LOOPS
*/
/**
* Loop through it
*
* @param {Function} callback
*/
public each(callback: Function): Node {
let name: string = this.name;
let response: iResponse = this.response;
if (this.isDomElement()) {
this.obj.each(function (index, el) {
el = $(el);
callback(
new Node(response, name + '[' + index + ']', el),
index
);
});
}
else if (this.isArray()) {
this.obj.forEach(function (el, index) {
callback(
new Node(response, name + '[' + index + ']', el),
index
);
});
}
else if (Flagpole.toType(this.obj) == 'object') {
let obj: {} = this.obj;
this.obj.keys().forEach(function (key) {
callback(
new Node(response, name + '[' + key + ']', obj[key]),
key
);
});
}
else if (Flagpole.toType(this.obj) == 'string') {
this.obj.toString().trim().split(' ').forEach(function (word, index) {
callback(
new Node(response, name + '[' + index + ']', word),
index
);
});
}
return this;
}
/**
* Loops through the element and expects the return from every callback to be true
*
* @param {Function} callback
*/
public every(callback: Function): Node {
let name: string = this.name;
let response: iResponse = this.response;
let every: boolean = true;
let node: Node = this;
this.response.ignore(function () {
if (node.isDomElement()) {
node.obj.each(function (index, el) {
el = $(el);
let element: Node = new Node(response, name + '[' + index + ']', el);
if (!callback(element)) {
every = false;
}
});
}
else if (node.isArray()) {
every = node.obj.every(function (el, index) {
return callback(
new Node(response, name + '[' + index + ']', el)
);
});
}
else if (node.isObject()) {
let obj: {} = node.obj;
every = node.obj.keys().every(function (key) {
return callback(
new Node(response, name + '[' + key + ']', obj[key])
);
});
}
else if (node.isString()) {
every = node.obj.toString().trim().split(' ').every(function (word, index) {
return callback(
new Node(response, name + '[' + index + ']', word)
);
});
}
});
this.assert(every,
'Every ' + this.name + ' passed'
);
return this;
}
/**
* Loops through the element and expects the return from every callback to be true
*
* @param {Function} callback
*/
public some(callback: Function): Node {
let name: string = this.name;
let response: iResponse = this.response;
let some: boolean = false;
let node: Node = this;
this.response.ignore(function () {
if (node.isDomElement()) {
node.obj.each(function (index, el) {
el = $(el);
let element: Node = new Node(response, name + '[' + index + ']', el);
if (callback(element)) {
some = true;
}
});
}
else if (node.isArray()) {
some = node.obj.some(function (el, index) {
return callback(
new Node(response, name + '[' + index + ']', el)
);
});
}
else if (node.isObject()) {
let obj: {} = node.obj;
some = node.obj.keys().some(function (key) {
return callback(
new Node(response, name + '[' + key + ']', obj[key])
);
});
}
else if (node.isString()) {
some = node.obj.toString().trim().split(' ').some(function (word, index) {
return callback(
new Node(response, name + '[' + index + ']', word)
);
});
}
});
this.assert(some,
'Some ' + this.name + ' passed'
);
return this;
}
/**
* Alias for some
*
* @param callback
*/
public any(callback: Function): Node {
return this.some(callback);
}
/**
* ASSERTIONS
*/
/**
* Does this element have this class name?
*
* @param {string} className
*/
public hasClass(className: string): Node {
if (this.isDomElement()) {
this.assert(this.obj.hasClass(className),
this.name + ' has class ' + className
);
}
return this;
}
/**
* Is this object's value greater than this?
*
* @param {number} value
*/
public greaterThan(value: number): Node {
return this.assert(this.obj > value,
this.name + ' is greater than ' + value + ' (' + this.obj + ')'
);
}
/**
* Is this object's value greater than or equal to this?
*
* @param value
*/
public greaterThanOrEquals(value: any): Node {
return this.assert(this.obj >= value,
this.name + ' is greater than or equal to ' + value + ' (' + this.obj + ')'
);
}
/**
* Is this object's value less than this?
*
* @param {number} value
*/
public lessThan(value: number): Node {
return this.assert(this.obj < value,
this.name + ' is less than ' + value + ' (' + this.obj + ')'
);
}
/**
* Is this object's value less or equal to this?
*
* @param value
*/
public lessThanOrEquals(value: any): Node {
return this.assert(this.obj <= value,
this.name + ' is less than or equal to ' + value + ' (' + this.obj + ')'
);
}
public between(minValue: any, maxValue: any): Node {
return this.assert(this.obj >= minValue && this.obj <= maxValue,
this.name + ' is between ' + minValue + ' and ' + maxValue + ' (' + this.obj + ')'
);
}
/**
* Make an assertion
*
* @param statement
* @param passMessage
* @param failMessage
*/
public assert(statement: boolean, message: string, actualValue?: string): Node {
this.response.assert(statement, message, actualValue);
return this;
}
/**
* Does this object contain this? Works for strings, arrays, and objects alike
*
* @param {string} string
*/
public contains(string: string): Node {
let contains: boolean = false;
if (this.isArray()) {
contains = (this.obj.indexOf(string) >= 0);
}
else if (this.isObject()) {
contains = (this.obj.hasOwnProperty(string));
}
else if (!this.isNullOrUndefined()) {
contains = (this.toString().indexOf(string) >= 0);
}
return this.assert(contains,
this.name + ' contains "' + string + '"'
);
}
/**
* Alias for contains
*
* @param string
*/
public contain(string: string): Node {
return this.contains(string);
}
/**
* Test with regular expression
*
* @param {RegExp} pattern
*/
public matches(pattern: RegExp): Node {
let value: string = this.toString();
return this.assert(pattern.test(value),
this.name + ' matches ' + String(pattern),
value
);
}
/**
* Does it start with this value?
*
* @param {string} matchText
*/
public startsWith(matchText: string): Node {
let assert: boolean = false;
let value: string = '';
if (!this.isNullOrUndefined()) {
value = this.toString();
assert = (value.indexOf(matchText) === 0);
}
return this.assert(assert,
this.name + ' starts with "' + matchText + '"',
matchText
);
}
/**
* Does this end with this value?
*
* @param {string} matchText
*/
public endsWith(matchText: string): Node {
let assert: boolean = false;
let value: string = '';
if (!this.isNullOrUndefined()) {
value = this.toString();
assert = (value.indexOf(matchText) === value.length - matchText.length);
}
return this.assert(assert,
this.name + ' ends with "' + matchText + '"',
matchText
);
}
/**
* Does this objects type match this?
*
* @param {string} type
*/
public is(type: string): Node {
let myType: string = Flagpole.toType(this.obj);
return this.assert((myType == type.toLocaleLowerCase()),
this.name + ' is type ' + type,
myType
);
}
/**
* Does this element exist?
*/
public exists(): Node {
let exists: boolean = false;
if (this.isDomElement()) {
exists = (this.obj.length > 0);
}
else if (!this.isNullOrUndefined()) {
exists = true;
}
return this.assert(exists,
this.name + ' exists'
);
}
/**
* Is this object's value equal to this?
*
* @param value
* @param {boolean} permissiveMatching
*/
public equals(value: any, permissiveMatching: boolean = false): Node {
let matchValue: string = this.toString();
let equals: string = 'equals';
let messageValue: string = (typeof value == 'string') ? '"' + value + '"' : value;
if (permissiveMatching) {
value = value.toLowerCase().trim();
matchValue = matchValue.toLowerCase().trim();
equals = 'is similar to';
}
return this.assert(matchValue == value,
this.name + ' ' + equals + ' ' + messageValue,
matchValue
);
}
/**
* Is this object's value similar to this?
*
* @param value
*/
public similarTo(value: any): Node {
return this.equals(value, true);
}
/**
* See if this string is in the list of enum values
*/
public in(arrayOfValues: string[]): Node {
let value: string = this.toString();
return this.assert(
arrayOfValues.indexOf(value) >= 0,
this.name + ' is in list: ' + arrayOfValues.join(','),
value
);
}
}