Skip to content

Latest commit

 

History

History
1302 lines (971 loc) · 40.2 KB

introduction.md

File metadata and controls

1302 lines (971 loc) · 40.2 KB

Motivation | TOC | Expression Language


Introduction

What is Seanox aspect-js?

Inspired by the good experiences with JSF (Java Server Faces) in terms of functionality and easy integration into the markup, the idea for a similar client-side full-stack solution with a minimalistic and lightweight approach for the implementation of single-page applications (SPAs) was born.

Seanox aspect-js takes the declarative approach of HTML and extends it with expression language, reactive rendering, additional attributes, model-view-controller, view-model-binding, resource bundle (i18n/l10n), NoSQL-datasource, test environment and much more.

Features

  • Easy integration in markup and JavaScript (clean code)
    combinable with other JavaScript frameworks if they don't do the same thing do and use a different syntax
  • Lightweight implementation
    requires no additional frameworks
  • Component based architecture
  • Namespaces and domain concept
    for better structuring of components, modules and business logic
  • Modularization (supports imports at the runtime)
    component concept for smart/automatic loading of composite resources at runtime
  • Event handling
  • Expression Language
    meta-language extension with full JavaScript support
  • Reactivity rendering
    rendering reacts to changes in data objects and triggers partial rendering on consumers
  • Markup rendering
    supports: conditions, custom tags, events, filter, interval, iterate, rendering, resources messages, validation, ...
  • Markup hardening
    makes it difficult to manipulate the attributes in the markup
    non-visible components are removed from the DOM and only reinserted when used
  • Model View Controller (MVC) / Model View ViewModel (MVVM)
    supports view model binding and events
  • Sitemap for organizing the view into pages, faces and facets
    supports virtual paths and permission concepts
  • Resource Bundle / Resource Messages
    internationalization (i18n), localization (l10n) and text outsourcing
  • NoSQL datasource based on XML
    lightweight data management for aggregation / projection / transformation
  • Micro Frontends
    platform and framework for the implementation of micro-frontends
  • Test environment
    for automated unit tests and integration tests
  • ...

Contents Overview

Getting Started

The framework consists of one JavaScript file that can be included via the URL of a release channel or downloaded as a release.

Release channels continuously provide the latest final major versions. This way, Seanox aspect-js is always up to date.

Each release consists of different versions for different purposes. These versions are also always available in two variants. The standard is the compressed version for productive use. Optionally, the uncompressed and documented max variant is also available for development and error analysis.

To begin, create an HTML file, e.g. index.html and insert Seanox apect-js.

<!-- development version, includes helpful comments -->
<script src="https://cdn.jsdelivr.net/npm/@seanox/aspect-js/release/aspect-js-max.js"></script>

or

<!-- production and minimized version -->
<script src="https://cdn.jsdelivr.net/npm/@seanox/aspect-js/release/aspect-js.js"></script>

The framework has been developed for the implementation of modular and component-based single-page applications. Due to the automatic loading of resources, modules and data, a web server is required for use.

Scope

Seanox aspect-js works exclusively in the HTML element BODY, which itself is included.

Expression Language

Expressions or the Expression Language (EL) is a simple access to the client-side JavaScript and thus to the models and components in Seanox aspect-js. In the expressions the complete JavaScript API is supported, which is enhanced with additional keywords, so that also the numerous arithmetic and logical operators can be used.

The expression language can be used from the HTML element BODY on in the complete markup as free text, as well as in all attributes. Exceptions are the HTML elements STYLE and SCRIPT whose content is not supported by the expression Language. When used as free text, plain text is generated as output. Adding markup, especially HTML code, is not possible this way and is only supported with the attributes output and import.

<body lang="{{DataSource.locale}}">
  <p>
    Today is {{new Date().toDateString()}}
    and it's {{new Date().toLocaleTimeString()}} o'clock.
  </p>
</body>

Expressions are interpreted by the renderer that starts after the page is loaded. Thus, expressions may be visible when the page is loaded. Here it is recommended to use the attributes output and import, which cause a direct output to the inner HTML of the element.

<p output="Today is {{new Date().toDateString()}}
    and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>

Or the use of the attribute release, which makes HTML elements and their contents visible only after rendering.

<p release>
  Today is {{new Date().toDateString()}}
  and it's {{new Date().toLocaleTimeString()}} o'clock.
</p>

Expressions can create and use global variables at runtime.

{{now:new Date()}}
<p>
  Today is {{now.toDateString()}}
  and it's {{now.toLocaleTimeString()}} o'clock.
</p>

Learn more

Attributes

In Seanox aspect-js, the declarative approach is implemented with attributes that can be used and combined in all HTML elements starting from the HTML element BODY, which is included. The values of the attributes can be static or dynamic with the use of the expression language. If an attribute contains an expression, the value is updated by the renderer with each refresh (render cycle) based on the initial expression.

Learn more

output

Sets for the HTML element the value or result of its expression as inner HTML. The behavior is similar to the import attribute, except that the output is updated with each render cycle. Supported values are text, one or more elements as NodeList or Array, as well as absolute or relative URLs to a remote resource and also the DataSource-URL (locator) for transformed content from the DataSource.

The output attribute can be combined with the condition attribute and will then only be executed if the condition is true.

<p output="Today is {{new Date().toDateString()}}
    and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>

<p output="xml:/example/content">
  loading resource...
</p>

<p output="xml:/example/data xslt:/example/style">
  loading resource...
</p>

Learn more

import

Loads the content for the HTML element at runtime and inserts it as inner HTML. The behavior is similar to the output attribute, except that the import is done once and the import attribute is removed after successful loading. As value one or more elements are supported as NodeList or Array, as well as absolute or relative URLs to a remote resource and also the DataSource URL (locator) for transformed content from the DataSource.

The import attribute can be combined with the condition attribute and will then only be executed if the condition is true.

<p import="Today is {{new Date().toDateString()}}
    and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>

<p import="xml:/example/content">
  loading resource...
</p>

<p import="xml:/example/data xslt:/example/style">
  loading resource...
</p>

<p import="https://raw.githubusercontent.com/seanox/aspect-js/master/test/resources/import_c.htmlx">
  loading resource...
</p>

Learn more

condition

As a condition, the attribute specifies whether an element remains contained in the DOM. The expression specified as the value must explicitly return true to retain the element. If the return value is different, the element is temporarily removed from the DOM and can be reinserted later by refreshing the parent element if the expression returns true.

<article condition="{{Model.visible}}">
  ...
</article>

The use of the condition attribute in combination with embedded JavaScript is possible as SCRIPT element with the type composite/javascript as Composite JavaScript, because here the renderer has control over the script execution and not the browser.

<script type="composite/javascript" condition="{{Model.visible}}">
    ...
</script>

Learn more

interval

Activates an interval-controlled refresh of the HTML element without the need to actively trigger the refresh. The interval uses the inner HTML as a template from which updated content is generated and inserted with each interval cycle. The attribute expects milliseconds as value, which can also be formulated as expression, where invalid values cause console output. Processing is concurrent or asynchronous but not parallel. Processing will start after the specified time when a previously started JavaScript procedure has finished. Therefore, the interval should be understood as timely but not exact. The interval starts refreshing automatically and ends when:

  • the element no longer exists in the DOM
  • the condition attribute is used that is not true
<p interval="1000">
  {{new Date().toLocaleTimeString()}}
</p>

With the combination of interval and variable expression, the implementation of a permanent counter is very simple.

{{counter:0}}
<p interval="1000">
  {{counter:parseInt(counter) +1}}
  {{counter}}
</p>

The interval attribute can be used in combination with embedded JavaScript as composite JavaScript.

<script type="composite/javascript" interval="1000">
    console.log(new Date().toLocaleTimeString());
</script>

Learn more

iterate

Iterative output is based on lists, enumerations, and arrays. If an HTML element is declared as iterative, the inner HTML is used as a template from which updated content is generated and inserted with each interval cycle and inserted as inner HTML. As value for the attribute a variable expression is expected, for which a meta object will be created, which allows access to the iteration in the template. Thus, the variable expression iterate={{tempA:Model.list}} creates the meta object tempA = {item, index, data}.

const Model = {
    months: ["Spring", "Summer", "Autumn", "Winter"]
};
<select iterate={{months:Model.months}}>
  <option value="{{months.index}}">
    {{months.item}}
  </option>
</select>

Learn more

id

The ID (identifier) has an elementary meaning in Seanox aspect-js. It is the basis for view model binding and is used by SiteMap for faces and facets in the face flow and thus as a destination for virtual paths.

As with all attributes, the expression language can be used, with the difference that the attribute is only read at the beginning. Due to the view model binding, changes to an existing element at runtime have no effect as long as it exists in the DOM.

Learn more

composite

Marks an element in the markup as a Composite. Composites are essential components that require an identifier (ID / Composite ID).

<article id="example" composite>
  ...
</article>

As a component, composites are composed of various Ressourcen (markup, CSS, JS) that can be outsourced to the module directory based on the (composite) ID and are only loaded at runtime when used.

Composites are also the basis for view model binding, which involves connecting HTML elements in the markup (view) with corresponding JavaScript objects (models). Models are static JavaScript objects that provide data, states, and functions for the view, similar to managed beans and DTOs (Data Transfer Objects). The view as presentation and user interface for interactions and the model are primarily decoupled. For the MVVM (Model-View-ViewModel) approach, as an extension to the MVC (Model View Controller), the controller links views and models bidirectionally based on the composite IDsand so no manual implementation and declaration of events, interaction or synchronization is required.

The SiteMap uses composites as faces for the primary projection of JavaScript objects (models), which means that they can be used as targets for virtual paths in the face flow, which has a direct influence on the visibility of the composites. When SiteMap is active, composites can be marked with the attribute static, which makes a composite permanently visible as a face regardless of virtual paths.

Learn more

strict

The attribute can be combined with the attributes composite and validate.

In combination with the attribute composite, it specifies that when loading the resources (JS, CSS, HTML) for a component, the filename is used in its original notation. The default behavior without the attribute strict uses the composite id with a lowercase letter at the beginning.

Learn more

events

Binds one or more events to an HTML element. This allows event-driven synchronization of HTML elements with corresponding JavaScript objects (models), as well as validation of the data to be synchronized (see validate for more information) and event-driven control and refreshing of other HTML elements (see render for more information).

<span id="output1">{{#text1.value}}</span>
<input id="text1" type="text"
    events="input change" render="#output1"/>

As with all attributes, the expression language is applicable here, with the difference that changes at runtime have no effect, since the attribute or the value for the view model binding is only processed initially as long as the HTML element exists in the DOM.

Learn more

validate

The attribute validate requires the combination with the attribute events. Together they define and control the synchronization between the markup of a composite and the corresponding JavaScript object (model), where a property with the same name must exist as a target for synchronization.

The validation works in two steps and uses the standard HTML5 validation at the beginning. If this cannot determine deviations from the expected result or if no HTML5 validation is specified, the validation of the JavaScript object is used if the model provides a corresponding validate method boolean validate(element, value) and the element to be validated is embedded in a composite.

<form id="Model" composite>
  <input id="text1" type="text" placeholder="e-mail address"
      pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
      validate events="input change" render="#Model"/>
  <input type="submit" value="submit" validate events="click"/>
</form>

The synchronization and the default action of the browser are directly influenced by the validation. This can use for it four states as return values: true, not true, text, undefined/void.

Learn more

message

Message is an optional part of Validation and is used for text and error output in case of an unconfirmed validation. This requires a combination with the attributes validate and events.

<form id="Model" composite>
  <input id="text1" type="text" placeholder="e-mail address"
      pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
      validate message="Valid e-mail address required"
      events="input change" render="#Model"/>
  <input type="submit" value="submit" validate events="click"/>
</form>
<form id="Model" composite>
  <input id="text1" type="text" placeholder="e-mail address"
      pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
      validate message="{{Messages['Model.text1.validation.message']}}"
      events="input change" render="#Model"/>
  <input type="submit" value="submit" validate events="click"/>
</form>

Learn more

notification

Notification is an optional part of validation and specifies that the content of the message attribute is displayed as an info box (browser feature) on the corresponding element if validation is not successful. The attribute requires the combination with the validate, events and message attributes.

<form id="Model" composite>
  <input id="text1" type="text" placeholder="e-mail address"
      pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
      validate message="Valid e-mail address required" notification
      events="input change" render="#Model"/>
  <input type="submit" value="submit" validate events="click"/>
</form>

Learn more

render

The attribute requires the combination with the events attribute. Together they define which targets are refreshed by the renderer with which occurring events. The expected value is one or more space-separated CSS or Query selectors that define the targets.

<span id="output1">{{#text1.value}}</span>
<input id="text1" type="text"
    events="input change" render="#output1"/>

Alternatively, reactive rendering can be used, where changes in the data objects trigger a partial update of the view.

Learn more

release

Inverse indicator that an HTML element was rendered. The renderer removes this attribute when an HTML element is rendered. This effect can be used for CSS to display elements only in rendered state. A corresponding CSS rule is automatically added to the HEAD when the page is loaded.

<span release>{{'Show me after rendering.'}}</span>

Learn more

DataSource

DataSource is a NoSQL approach to data storage based on XML data in combination with multilingual data separation, optional aggregation and transformation. A combination of the approaches of a read only database and a CMS.

DataSource is based on static data. Therefore, the implementation uses a cache to minimize network access.

The data is queried via XPath. The result can be concatenated and aggregated and the result can be transformed with XSLT.

Learn more

Resource Bundle (Messages/i18n/l10n)

(Resource)Messages is a static DataSource extension for internationalization (i18n), localization (l10n) and client-related texts. The implementation is based on a set of key-value or label-value data which is stored in the locales.xml in the DataSource directory.

Learn more

Model View Controller

The Model View Controller (MVC) is a design pattern for separating interaction, data and presentation. A distinction should be made between I/O controller and application controller. The pure MVC design pattern means the I/O controller for the transmission of interactions. Since this is provided by the operating system and browser, Seanox aspect-js ostensibly refers to the application controller.

+------------------------------------------+--------------+-----------------------+
|  View                                    |  Controller  |  Model                |
+------------------------------------------+--------------+-----------------------+
|  Markup                                  |  Composite   |  JavaScript           |
|                                          |  Reactive    |                       |
+------------------------------------------+--------------+-----------------------+
|  <form id="model" composite>             |  aspect-js   |  const model = {      |
|    <input id="message" events="input"/>  |              |      message: "",     | 
|    <button id="submit"/>                 |              |      submit: {        |
|  </form>                                 |              |          onClick() {  |
|                                          |              |          }            |
|                                          |              |      }                |
|                                          |              |  }                    |
+------------------------------------------+--------------+-----------------------+

Learn more

Model

Models are representable/projectable static JavaScript objects that can provide and receive data, states and interactions for views, comparable to managed beans and DTOs (Data Transfer Objects). As singletons/facades/delegates, they can use other components and abstractions, contain business logic themselves, and be a link between the user interface (view) and middleware (backend).

Learn more

View

The view is exclusively responsible for the representation or projection of a model. Where projection is an important term because the way a model is represented is not restricted.

Learn more

Controller

The (application)controller controls internal processes within an application (face flow) and takes over the data flow between view and model with the view-model binding, whereby we can also speak of MVVM (Model-View-ViewModel) and MVCS (Model-View-Controller-Service) here.

Learn more

View Model Binding

The view-model binding takes over the bidirectional linking of the HTML elements of the view with the models as static JavaScript objects and thus organizes the data flow, communicates events as well as states and binds functions.

Learn more

Composite

The basis for view-model binding is formed by composites, which are functionally independent components consisting of markup, CSS, JavaScript, and optionally other resources. All components are bound via, the also called Composite ID, ID of the HTML construct, the model with the same name in JavaScript and the ID corresponding to the HTML in CSS.

<!DOCTYPE HTML>
<html>
  <head>
    <script src="aspect-js.js"></script>
  </head>
  <body>
    <div id="example" composite></div>
  </bod>
</html>
const example = {
    ...    
}
#example {
  ...    
}

The Composite ID is thus a unique identifier within the application. It is a string of letters, numbers, and underscores that is at least one character long, begins with an underscore or a letter, and is formed by combining the id and composite attributes on an HTML element.

Composites, or better their composite ID, define the point in the global object tree where the corresponding JavaScript model is located. All HTML elements with an ID enclosed by a composite are then reflected as branches in the corresponding JavaScript model if they are implemented accordingly in the model.

const model = {
    message: "Hello", 
    submit: {
        ...    
    }
};
<html>
  <body>
    <form id="model" composite>
      <input type="text" id="message"/>
      <input type="submit" id="submit"/>
      ...
    </form>
  </body>
</html>

Learn more

Binding

View-model binding is about linking markup/HTML (view) to the corresponding JavaScript object (model). The binding passes interactions and state changes of the view to the model and provides an interface for middleware functions and services for the view. In this way, no manual implementation of events, synchronization and interaction between view and application logic is required.

const model = {
    message: "Hello", 
    dock() {
        ...
    },
    undock() {
        ...
    },
    submit: {
        onClick(event) {
            ...
        }
    }
};
<html>
  <body>
    <form id="model" composite>
      <input type="text" id="message" value="{{model.message}}" events="change"/>
      <input type="submit" id="submit"/>
      ...
    </form>
  </body>
</html>

Learn more

Dock / Undock

If a composite is used/inserted in the DOM, the corresponding model is docked/linked and when removing from the DOM, the corresponding model is undocked/unlinked. In both cases the model can optionally implement appropriate methods. The method dock is executed before rendering, before inserting the composite into the DOM, or after loading the page during initial rendering, and can be used to prepare the view. The method undock is executed after the composite is removed from the DOM and can be used to postprocess/clean the view.

const model = {
    dock() {
        ...
    },
    undock() {
        ...
    }
};
<html>
  <body>
    <div id="model" composite>
      ...
    </div>
  </body>
</html>

For composites in combination with a condition, the call of the methods depends on the result of the condition.

Learn more

Synchronization

In addition to the static linking and assignment of HTML elements to JavaScript objects (models), the view model binding also includes the synchronization of values between the HTML elements and the fields of the JavaScript object. The synchronization depends on events that are declared for the HTML element with the attribute events and so the synchronization is executed only when one of the defined events occurs.

Learn more

Validation

The synchronization of values between view (HTML elements) and the fields of JavaScript models can be monitored and controlled by validation. Validation is declared in HTML via the validate attribute in combination with the events attribute and requires a corresponding validation method in the JavaScript object (model).

Learn more

Events

Events, more precisely the interaction between view and model, are also considered during view model binding. The methods for interaction will be implemented only in the model. In the markup itself, no declaration is required. The view model binding knows the available events for HTML. During binding, the model is searched for corresponding methods that will be registered as event listeners.

const contact = {
    mail: {
        onClick(event) {
          const mail = "mailto:mail@local?subject=Test&body=Greetings";
            document.location.href = mail;
            return false;
        }
    }
};
<html>
  <body>
    <div id="contact" composite static>
      <p>
        Example for use of events.
      </p>
      <button id="mail">
        Click Me!
      </button>
    </div>
  </body>
</html>

Learn more

SiteMap

The representation of the page can be organized in Seanox aspect-js with SiteMap into faces as well as facets and addressed via virtual paths. For this purpose SiteMap provides a hierarchical directory structure based on the virtual paths of all faces and facets. SiteMap controls the access and visualization (showing and hiding) of the element -- the so-called face flow. Face flow and visualization work resolutely and actively use the DOM to insert and remove the faces and facets.

+-----------------------------------------------+
|  Page                                         |
|  +-----------------------------------------+  |
|  |  Face A / Partial Face A                |  |
|  |  +-------------+       +-------------+  |  |
|  |  |  Facet A1   |  ...  |  Facet An   |  |  |
|  |  +-------------+       +-------------+  |  |
|  |                                         |  |
|  |  +-----------------------------------+  |  |
|  |  |  Face AA                          |  |  |
|  |  |  +-----------+     +-----------+  |  |  |
|  |  |  | Facet AA1 | ... | Facet AAn |  |  |  |
|  |  |  +-----------+     +-----------+  |  |  |
|  |  +-----------------------------------+  |  |
|  |  ...                                    |  |
|  +-----------------------------------------+  |
|  ...                                          |
|  +-----------------------------------------+  |
|  |  Face n                                 |  |
|  |  ...                                    |  |
|  +-----------------------------------------+  |
+-----------------------------------------------+

Page

In a single page application, the page is the elementary framework and runtime environment of the entire application.

Face

A face is the primary projection of models/components/content. This projection can contain additional substructures in the form of faces and sub-faces. Thus, parent faces become partial faces if the path refers to a sub-face. In this case, all parent faces are displayed partially, i.e. without their possibly contained faces.

Facet

Facets are parts of a face (projection) and usually not an independent representation. For example, in a search form, the input mask and the result table can be separate faces of a face. Both faces and facets are accessible via virtual paths. The path to facets causes the enclosing face to be displayed with all its parent faces.

Face Flow

Face flow describes the access control and the sequence of faces and facets. The SiteMap provides interfaces, permission concepts and acceptors with which the face flow can be controlled and influenced.

Learn more

Navigation

The navigation can be effected by changing the URL hash in the browser (direct input), by using hash links, and in JavaScript with window.location.hash, window.location.href, SiteMap.navigate(path) and SiteMap.forward(path).

<a href="#a#b#c">Goto root + a + b + c</a>
<a href="##">Back to the parent</a>
<a href="##x">Back to the parent + x</a>
SiteMap.navigate("#a#b#c");
SiteMap.navigate("##");
SiteMap.navigate("##x");
SiteMap.forward("#a#b#c");
SiteMap.forward("##");
SiteMap.forward("##x");

In difference to the navigate method, the forwarding is executed directly instead of triggering an asynchronous forwarding by changing the location hash.

Relative paths without hash at the beginning are possible, but only work with SiteMap.navigate(path).

Learn more

Permission Concept

The permission concept is based on permit methods, which are defined as callback methods with the configuration of the face-flow. Several permit methods can be defined, which are verified with each requested path. Only if all permit methods confirm the requested path with true, it will be used and the renderer will render the dependent faces and facets and makes them visible.

Learn more

Virtual Paths

Virtual paths are used for navigation and control of face-flow. The target can be a face, a facet or a function. For SPAs (Single-Page-Applications) the anchor part of the URL is used for the paths.

https://example.local/example/#path

According to the file system, absolute, relative and additionally functional paths are also supported here. Paths consist exclusively of word characters, underscores and optionally the minus character (based on combined) IDs. The hash character is used as separator and root. Spaces are not supported.

Learn more

Components

Seanox aspect-js is designed for a modular and component-based architecture. For this purpose, the framework supports declarative marking of components in the markup as well as automatic mechanisms for view-model binding and loading of outsourced resources at runtime.

Learn more

Scripting

Seanox aspect-js uses composite JavaScript. A dialect based on the browser JavaScript, enriched with macros -- a simple meta-syntax.

Composite JavaScript, which includes the modules, is not inserted as an element, but executed directly with the eval method. Since an isolated and not the global scope is used for this, variables, constants and methods are not directly usable globally or across, which is why, among other things, the Composite-JavaScript was enriched with macros, which take over such tasks, among other things.

Learn more

Reactivity Rendering

In the reactive approach, changes to the data objects (models) trigger a partial refresh of the consumers in the view. Consumers are all expressions that have read access to the changed value of a data object. In the view, expressions can be used in the HTML elements and in the free text. The data objects must then use Reactive(object) or Object.prototype.reactive() for reactive rendering.

const Model = {
    value: ...
}.reactive();

In this example, the renderer automatically updates all HTML elements in the DOM, which includes free-text that uses the value property from the model directly or indirectly in an expression when the value of the value property changes or, more precisely, when a changed value has been set final in the data object, which can be relevant when using getters and setters.

Reactive works permanently recursively on all object levels and also on the objects which are added later as values. Even if these objects do not explicitly use Reactive, new instances are created for the referenced objects. Initiating objects and reactive models are logically decoupled and are synchronized bidirectionally. In contrast, views and reactive models, like models internally, use proxies that behave and can be used like the initiating originals. However, proxies are separate instances that are compatible but not identical to the initiating object. This logical separation is necessary so that the renderer can generate appropriate notifications when data changes and thus update the consumers in the view.

Learn more

API Extensions

The JavaScript API for Seanox aspect-js has been extended by some general functions.

Learn more

Events

Seanox aspect-js provides various events that can be used to implement extensions and to notify the application of certain operating states of the framework and runtime environment.

Learn more

Test

The Test API supports the implementation and execution of integration tests and can be used for suites, scenarios and single test cases.

As a modular part of Seanox aspect-js, the Test API is included in all releases except the core versions. Because the test API causes some special features in terms of error handling and console output, the test API has to be activated consciously at runtime.

Test.activate();

Test.create({test() {
    ...
}});

Test.start();

Learn more

Task

The smallest component in an integration test, used here as 'task', because 'case' is a keyword in JavaScript. It can be implemented alone, but is always used in a scenario.

Test.activate();

Test.create({test() {
    Assert.assertTrue(true);
}});

Test.start();

Task is primarily a meta object.

{name:..., test:..., timeout:..., expected:..., ignore:...}

Learn more

Scenario

A scenario is a sequence of a lot of test cases (tasks).

Test.activate();

Test.create({test() {
    Assert.assertTrue(true);
}});
Test.create({name:"example", timeout:1000, test() {
    Assert.assertTrue(true);
}});
Test.create({error:Error test() {
    throw new Error();
}});
Test.create({error:/^My Error/i, test() {
    throw new Error("My Error");
}});
Test.create({ignore:true, test() {
    Assert.assertTrue(true);
}});

Test.start();

Learn more

Suite

A suite is a complex bundle of different test cases, scenarios and other suites. Usually a suite consists of different files, which then represent a complex test. An example of a good suite is a cascade of different files und the test can be started in any file and place. This makes it possible to perform the integration test on different levels and with different complexity.

Learn more

Assert

The test cases are implemented with assertions. The Test API provides elementary assertions, you can implement more. The function is simple. If an assertion was not true, an error is thrown.

Test.activate();

Test.create({test() {

    Assert.assertTrue(true);
    Assert.assertTrue("message", true);

    Assert.assertFalse(false);
    Assert.assertFalse("message", false);

    Assert.assertEquals(expected, value);
    Assert.assertEquals("message", expected, value);

    Assert.assertNotEquals(unexpected, value);
    Assert.assertNotEquals("message", unexpected, value);

    Assert.assertSame(expected, value);
    Assert.assertSame("message", expected, value);

    Assert.assertNotSame(unexpected, value);
    Assert.assertNotSame("message", unexpected, value);

    Assert.assertNull(null);
    Assert.assertNull("message", null);

    Assert.assertNotNull(null);
    Assert.assertNotNull("message", null);

    Assert.fail();
    Assert.fail("message");
}});

Test.start();

Learn more

Configuration

Optionally, the Test API can be configured with each start. A meta object is expected as parameter. The configuration contained in it is partially adopted and the unknown is ignored.

Test.start({auto: boolean, ouput: {...}, monitor: {...}});

Learn more

Monitoring

Monitoring monitors the progress of the test and is informed of the various steps and statuses during execution. The monitor is optional. Without this, the console is used to output information about the test process.

Learn more

Control

The test progress and the execution of the tests can be controlled by the Test API.

Test.start();
Test.start({auto: boolean});

The start can be done manually or when using auto = true, by loading the page. If the page is already loaded, the parameter auto is ignored and the start is executed immediately.

Further methods for controlling and monitoring test execution are available.

Learn more

Events

Events and their callback methods are another way of monitoring test execution. The callback methods are registered at the Test API for corresponding events and then work similar to the monitor.

Test.listen(Test.EVENT_START, function(event, status) {
    ...
});

Test.listen(Test.EVENT_PERFORM, function(event, status) {
    ...
});

Test.listen(Test.EVENT_FINISH, function(event, status) {
    ...
});

Learn more


Motivation | TOC | Expression Language