├── .editorconfig
├── .gitignore
├── .travis.yml
├── README.md
├── angular-example
├── .gitignore
├── README.md
├── app
│ ├── app.html
│ ├── app.module.ts
│ ├── app.ts
│ ├── bootstrap.ts
│ ├── models
│ │ └── todo.ts
│ └── services
│ │ ├── localStorage.ts
│ │ └── store.ts
├── bs-config.json
├── index.html
├── intern.json
├── package-lock.json
├── package.json
├── systemjs-angular-loader.ts
├── systemjs.config.ts
├── tests
│ ├── app.ts
│ ├── functional
│ │ └── Todo.ts
│ ├── index.html
│ ├── intern-angular-shim.ts
│ ├── services
│ │ └── store.ts
│ ├── systemjs-istanbuljs.ts
│ └── systemjs.config.ts
└── tsconfig.json
├── angularjs-example
├── README.md
├── index.html
├── js
│ ├── app.js
│ ├── controllers
│ │ └── todoCtrl.js
│ ├── directives
│ │ ├── todoBlur.js
│ │ ├── todoEscape.js
│ │ └── todoFocus.js
│ └── services
│ │ └── todoStorage.js
├── package.json
└── tests
│ ├── all.js
│ ├── controllers
│ └── todoCtrl.js
│ ├── directives
│ └── todoBlurFocus.js
│ ├── functional
│ └── Todo.js
│ └── intern.js
├── backbone-example
├── README.md
├── index.html
├── js
│ ├── app.js
│ ├── collections
│ │ └── todos.js
│ ├── models
│ │ └── todo.js
│ ├── routers
│ │ └── router.js
│ └── views
│ │ ├── app-view.js
│ │ └── todo-view.js
├── package.json
└── tests
│ ├── all.js
│ ├── functional
│ └── Todo.js
│ ├── intern.js
│ └── models
│ └── todo.js
├── dojo-example
├── README.md
├── css
│ └── app.css
├── index.html
├── intern.json
├── js
│ └── todo
│ │ ├── CssToggleWidget.js
│ │ ├── TodoList.js
│ │ ├── app.html
│ │ ├── app.js
│ │ ├── app18.html
│ │ ├── app18.js
│ │ ├── ctrl
│ │ ├── RouteController.js
│ │ ├── TodoListRefController.js
│ │ ├── TodoRefController.js
│ │ └── _HashCompletedMixin.js
│ │ ├── form
│ │ ├── CheckBox.js
│ │ └── InlineEditBox.js
│ │ ├── misc
│ │ ├── HashSelectedConverter.js
│ │ └── LessThanOrEqualToConverter.js
│ │ ├── model
│ │ ├── SimpleTodoModel.js
│ │ └── TodoModel.js
│ │ └── store
│ │ └── LocalStorage.js
├── package-lock.json
├── package.json
└── tests
│ ├── all.js
│ ├── form
│ └── CheckBox.js
│ ├── functional
│ └── Todo.js
│ ├── model
│ └── SimpleTodoModel.js
│ └── store
│ └── LocalStorage.js
├── electron-example
├── .babelrc
├── .gitignore
├── README.md
├── intern.json
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── actions
│ │ └── index.js
│ ├── bootstrap.js
│ ├── components
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ ├── MainSection.js
│ │ ├── TodoItem.js
│ │ └── TodoTextInput.js
│ ├── constants
│ │ ├── ActionTypes.js
│ │ └── TodoFilters.js
│ ├── containers
│ │ └── App.js
│ ├── index.js
│ ├── main.js
│ ├── reducers
│ │ ├── index.js
│ │ └── todos.js
│ └── renderer.ts
└── tests
│ ├── build_check.js
│ ├── functional
│ ├── app.js
│ └── spectron.js
│ ├── pre.js
│ └── unit
│ └── actions
│ └── index.js
├── grunt-example
├── Gruntfile.js
├── README.md
├── app
│ ├── Block.js
│ └── index.html
├── package-lock.json
├── package.json
└── tests
│ └── lib
│ ├── add.js
│ └── get.js
├── jquery-example
├── README.md
├── css
│ └── app.css
├── index.html
├── intern.json
├── js
│ ├── app.js
│ └── utils.js
├── package-lock.json
├── package.json
└── tests
│ ├── functional
│ └── Todo.js
│ └── utils.js
├── parallel-example
├── README.md
├── index.html
├── intern.js
├── package.json
├── parallel.sh
└── tests
│ └── functional
│ ├── sample1.js
│ └── sample2.js
├── phantomjs-example
├── README.md
├── package.json
└── tests
│ ├── intern.js
│ └── test_example.js
├── react-enzyme-example
├── .babelrc
├── .gitignore
├── README.md
├── intern.json
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── actions
│ │ └── index.js
│ ├── components
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ ├── MainSection.js
│ │ ├── TodoItem.js
│ │ └── TodoTextInput.js
│ ├── constants
│ │ ├── ActionTypes.js
│ │ └── TodoFilters.js
│ ├── containers
│ │ └── App.js
│ ├── index.js
│ └── reducers
│ │ ├── index.js
│ │ └── todos.js
└── tests
│ ├── build_check.js
│ ├── functional
│ └── Todo.js
│ └── unit
│ ├── actions
│ └── index.js
│ ├── components
│ └── Header.js
│ └── reducers
│ └── todos.js
├── react-example
├── README.md
├── index.html
├── js
│ ├── app.jsx
│ ├── footer.jsx
│ ├── todoItem.jsx
│ ├── todoModel.js
│ └── utils.js
├── package.json
└── tests
│ ├── all.js
│ ├── functional
│ └── Todo.js
│ ├── intern.js
│ ├── support
│ └── jsx.js
│ └── todoModel.js
├── run_all.sh
├── travis-ci-example
├── README.md
├── app
│ └── App.js
├── package.json
└── tests
│ ├── intern.js
│ └── lib
│ └── demo.js
├── typescript-example
├── .gitignore
├── README.md
├── index.html
├── intern.json
├── package-lock.json
├── package.json
├── src
│ ├── app.ts
│ ├── collections
│ │ └── todos.ts
│ ├── config.ts
│ ├── models
│ │ └── todo.ts
│ ├── routers
│ │ └── router.ts
│ └── views
│ │ ├── app-view.ts
│ │ └── todo-view.ts
├── tests
│ ├── functional
│ │ └── Todo.ts
│ ├── tsconfig.json
│ └── unit
│ │ ├── models
│ │ └── todo.ts
│ │ └── routers
│ │ └── router.ts
└── tsconfig.json
└── webpack-example
├── .gitignore
├── README.md
├── index.html
├── intern.json
├── package-lock.json
├── package.json
├── src
├── app.js
├── collections
│ └── todos.js
├── globals.d.ts
├── models
│ └── todo.ts
├── routers
│ └── router.ts
└── views
│ ├── app-view.js
│ └── todo-view.js
├── tests
├── all.js
├── functional
│ └── Todo.js
├── intern.js
├── models
│ └── todo.js
├── routers
│ └── router.js
└── tests.js
├── tsconfig.json
└── webpack.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.md]
4 | indent_style = space
5 | max_line_length = 120
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | sauce_connect.log*
2 | node_modules
3 | html-report
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6.5.0'
4 | script: cd travis-ci-example && npm install && npm test
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # intern-examples
2 |
3 | [](https://github.com/theintern/intern/tree/3.4/)
4 | [](https://github.com/theintern/intern/tree/master/)
5 | 
6 |
7 |
8 |

9 |
10 | [Intern](https://github.com/theintern/intern) is a complete test system for JavaScript designed to help you write and run consistent, high-quality test cases for your JavaScript libraries and applications. It can be used to test _any_ JavaScript code.
11 |
12 |
13 | This repository is a collection of examples of using Intern in web applications. Use these examples as your guide to integrate Intern into your projects! Every example has a README that will guide you through the process of setting it up.
14 |
15 | ## Intern 4 Examples
16 |
17 | * [Dojo](./dojo-example)
18 | * [Electron](./electron-example)
19 | * [jQuery](./jquery-example)
20 | * [React with Enzyme](./react-enzyme-example)
21 | * [TypeScript](./typescript-example)
22 | * [Grunt](./grunt-example)
23 |
24 | ## Intern 3 Examples
25 |
26 | Each of the examples can be switched from running tests locally to using a cloud testing provider by setting the relevant [Cloud testing Intern settings](https://theintern.github.io/intern/#hosted-selenium) within the Intern config for that example.
27 |
28 | * [Backbone](./backbone-example)
29 | * [React](./react-example)
30 | * [Parallelized Intern](./parallel-example)
31 | * [Travis CI](./travis-ci-example)
32 |
33 | ## External Examples
34 |
35 | Intern 4 hasn’t been out for very long yet, so most of these are still based on Intern 3.
36 |
37 | * [Cassowary JS](https://github.com/slightlyoff/cassowary.js/)
38 | * [jQuery PEP](https://github.com/jquery/PEP/tree/master/tests)
39 | * [Official Intern Tutorial](https://github.com/theintern/intern-tutorial)
40 | * [Tutorial from ArcGIS](https://github.com/stdavis/intern-tutorial-esri-jsapi)
41 | * [Firefox Accounts JS Client testing example](https://github.com/mozilla/fxa-js-client/tree/master/tests)
42 |
43 | ## Contributing
44 |
45 | We welcome contributions of new examples, or improvements/updates to existing examples. Just fork this repo, add your example to a new branch, and make a PR. Note that like most open source projects, we require everyone to sign a [contributor license agreement](https://js.foundation/CLA/) when making non-trivial PRs.
46 |
47 |
48 | © [SitePen, Inc.](http://sitepen.com) and its [contributors](https://github.com/theintern/intern/graphs/contributors)
49 |
50 |
--------------------------------------------------------------------------------
/angular-example/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | *.js
3 | *.js.map
4 | typings
5 |
--------------------------------------------------------------------------------
/angular-example/README.md:
--------------------------------------------------------------------------------
1 | angular2-example
2 | ================
3 |
4 | This example is based on the Angular 2 TodoMVC implementation by [Sam Saccone](http://github.com/samccone).
5 |
6 | ## Setup
7 |
8 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
9 |
10 | 2. Install node modules
11 | ```
12 | $ npm install
13 | ```
14 |
15 | 3. Build the example
16 | ```
17 | $ npm run build
18 | ```
19 |
20 | ## Running tests
21 |
22 | **Unit tests in Node**
23 |
24 | $ npm test
25 |
26 | **WebDriver tests**
27 |
28 | $ npm test webdriver
29 |
--------------------------------------------------------------------------------
/angular-example/app/app.html:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/angular-example/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { FormsModule } from '@angular/forms';
4 |
5 | import { LocalStorageService, LocalStorageServiceKey } from './services/localStorage';
6 | import { TodoService } from './services/store';
7 | import { TodoApp } from './app';
8 |
9 | @NgModule({
10 | imports: [
11 | BrowserModule,
12 | FormsModule
13 | ],
14 | declarations: [ TodoApp ],
15 | providers: [
16 | { provide: LocalStorageServiceKey, useValue: 'angular-todos' },
17 | LocalStorageService,
18 | TodoService
19 | ],
20 | bootstrap: [ TodoApp ]
21 | })
22 | export class AppModule { }
23 |
--------------------------------------------------------------------------------
/angular-example/app/app.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { TodoService } from './services/store';
3 | import { Todo } from './models/todo';
4 |
5 | @Component({
6 | selector: 'todo-app',
7 | templateUrl: './app.html'
8 | })
9 | export class TodoApp {
10 | private todoStore: TodoService;
11 | private todos: Todo[] = [];
12 |
13 | private remaining = 0;
14 | private completed = 0;
15 |
16 | newTodoText = '';
17 |
18 | constructor(todoStore: TodoService) {
19 | this.todoStore = todoStore;
20 | }
21 |
22 | ngOnInit() {
23 | this.todoStore.get().subscribe(todos => this.initialize(todos));
24 | }
25 |
26 | private initialize(todos: Todo[]) {
27 | this.todos = todos;
28 |
29 | this.remaining = 0;
30 | this.completed = 0;
31 |
32 | todos.forEach(todo => {
33 | if (todo.completed) {
34 | this.completed++;
35 | } else {
36 | this.remaining++;
37 | }
38 | });
39 | }
40 |
41 | setAllTo(value: boolean) {
42 | this.todoStore.set(
43 | this.todos.map(todo => {
44 | todo.completed = value;
45 | return todo;
46 | })
47 | )
48 | .subscribe(todos => this.initialize(todos))
49 | ;
50 | }
51 |
52 | cancelEditingTodo(todo: Todo) {
53 | todo.editing = false;
54 | }
55 |
56 | updateEditingTodo(todo: Todo, editedTitle: string) {
57 | editedTitle = editedTitle.trim();
58 | todo.editing = false;
59 |
60 | let observable;
61 | if (editedTitle.length === 0) {
62 | observable = this.todoStore.delete(todo);
63 | } else {
64 | todo.title = editedTitle;
65 | observable = this.todoStore.update(todo);
66 | }
67 |
68 | observable.subscribe(todos => this.initialize(todos));
69 | }
70 |
71 | editTodo(todo: Todo) {
72 | todo.editing = true;
73 | }
74 |
75 | removeCompleted() {
76 | this.todoStore.delete(this.todos.filter(todo => todo.completed))
77 | .subscribe(todos => this.initialize(todos))
78 | ;
79 | }
80 |
81 | toggleCompletion(todo: Todo) {
82 | todo.completed = !todo.completed;
83 | this.todoStore.set(this.todos)
84 | .subscribe(todos => this.initialize(todos))
85 | ;
86 | }
87 |
88 | remove(todo: Todo){
89 | this.todoStore.delete(todo)
90 | .subscribe(todos => this.initialize(todos))
91 | ;
92 | }
93 |
94 | addTodo() {
95 | if (this.newTodoText.trim().length) {
96 | this.todoStore.add(this.newTodoText)
97 | .subscribe(todos => this.initialize(todos))
98 | ;
99 | this.newTodoText = '';
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/angular-example/app/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app.module';
4 |
5 | platformBrowserDynamic().bootstrapModule(AppModule);
6 |
--------------------------------------------------------------------------------
/angular-example/app/models/todo.ts:
--------------------------------------------------------------------------------
1 | export interface TodoObject {
2 | completed?: boolean;
3 | editing?: boolean;
4 | readonly id?: number;
5 | title: string;
6 | }
7 |
8 | export class Todo implements TodoObject {
9 | readonly id: number;
10 | completed: boolean;
11 | editing: boolean;
12 | title: string;
13 |
14 | constructor({ title, id = Date.now(), editing = false, completed = false }: TodoObject) {
15 | this.id = id;
16 | this.completed = completed;
17 | this.editing = editing;
18 | this.title = title.trim();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/angular-example/app/services/localStorage.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject, InjectionToken } from '@angular/core';
2 |
3 | export const LocalStorageServiceKey = new InjectionToken('LocalStorageServiceKey');
4 |
5 | @Injectable()
6 | export class LocalStorageService {
7 | constructor(@Inject(LocalStorageServiceKey) private key: string) {}
8 |
9 | get(): T {
10 | return JSON.parse(localStorage.getItem(this.key) || 'null');
11 | }
12 |
13 | set(value: T) {
14 | localStorage.setItem(this.key, JSON.stringify(value));
15 | }
16 |
17 | clear() {
18 | localStorage.removeItem(this.key);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/angular-example/app/services/store.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Todo, TodoObject } from '../models/todo';
3 | import { LocalStorageService } from './localStorage';
4 |
5 | import { Observable } from 'rxjs/Observable';
6 | import 'rxjs/add/observable/of';
7 | import 'rxjs/add/operator/map';
8 |
9 | export { Todo, TodoObject }
10 |
11 | @Injectable()
12 | export class TodoService {
13 | constructor(private storage: LocalStorageService) {}
14 |
15 | get(): Observable {
16 | const objects = this.storage.get() || [];
17 | return Observable.of(objects.map(object => new Todo(object)));
18 | }
19 |
20 | add(title: string): Observable {
21 | const objects = this.storage.get() || [];
22 | objects.push(new Todo({ title }));
23 |
24 | this.storage.set(objects);
25 |
26 | return this.get();
27 | }
28 |
29 | update(todo: Todo): Observable {
30 | const objects = this.storage.get() || [];
31 | const index = objects.findIndex(object => object.id === todo.id);
32 |
33 | if (index === -1) {
34 | objects.push(todo);
35 | } else {
36 | objects.splice(index, 1, todo);
37 | }
38 |
39 | this.storage.set(objects);
40 |
41 | return this.get();
42 | }
43 |
44 | set(todos: Todo[]): Observable {
45 | this.storage.set(todos);
46 | return this.get();
47 | }
48 |
49 | delete(todos: Todo | Todo[]): Observable {
50 | if (!Array.isArray(todos)) {
51 | todos = [ todos ];
52 | }
53 |
54 | const ids = new Set(todos.map(todo => todo.id));
55 | const objects = (this.storage.get() || []).filter(todo => !ids.has(todo.id));
56 |
57 | this.storage.set(objects);
58 |
59 | return this.get();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/angular-example/bs-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "./**/*.js",
4 | "./intern.json"
5 | ],
6 | "open": false,
7 | "logLevel": "silent",
8 | "logConnections": false,
9 | "server": {
10 | "baseDir": ".",
11 | "middleware": {
12 | "0": null
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/angular-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular2 • TodoMVC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/angular-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "environments": "chrome",
3 | "functionalSuites": "dist/tests/functional/Todo.js",
4 | "browser": {
5 | "loader": {
6 | "script": "systemjs"
7 | },
8 | "plugins": [
9 | {
10 | "script": "dist/tests/intern-angular-shim.js",
11 | "useLoader": true,
12 | "options": {
13 | "appConfig": "dist/systemjs.config.js",
14 | "testConfig": "dist/tests/systemjs.config.js",
15 | "require": [
16 | "node_modules/core-js/client/shim.js",
17 | "node_modules/zone.js/dist/zone.js",
18 | "node_modules/zone.js/dist/long-stack-trace-zone.js"
19 | ]
20 | }
21 | }
22 | ],
23 | "suites": [
24 | "dist/tests/services/store.js",
25 | "dist/tests/app.js"
26 | ]
27 | },
28 | "excludeInstrumentation": "(?:node_modules|browser|tests)\\/|dist\\/[^\\/]+\\.js"
29 | }
30 |
--------------------------------------------------------------------------------
/angular-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "dependencies": {
4 | "@angular/common": "~4.3.0",
5 | "@angular/compiler": "~4.3.0",
6 | "@angular/core": "~4.3.0",
7 | "@angular/forms": "~4.3.0",
8 | "@angular/platform-browser": "~4.3.0",
9 | "@angular/platform-browser-dynamic": "~4.3.0",
10 | "@angular/router": "~4.3.0",
11 | "core-js": "~2.4.1",
12 | "rxjs": "~5.4.2",
13 | "systemjs": "0.19.39",
14 | "todomvc-app-css": "2.0.0",
15 | "todomvc-common": "1.0.1",
16 | "tslib": "~2.0.1",
17 | "zone.js": "~0.8.14"
18 | },
19 | "devDependencies": {
20 | "@types/sinon": "~2.3.3",
21 | "@types/systemjs": "0.20.2",
22 | "concurrently": "~3.5.0",
23 | "cpx": "~1.5.0",
24 | "intern": "^4.8.7",
25 | "lite-server": "~2.3.0",
26 | "sinon": "~2.3.8",
27 | "typescript": "~4.0.2"
28 | },
29 | "scripts": {
30 | "copy": "cpx \"app/**/*.{html,css}\" dist/app",
31 | "copy:watch": "cpx \"app/**/*.{html,css}\" dist/app --watch",
32 | "compile": "tsc",
33 | "compile:watch": "tsc -w",
34 | "build": "concurrently \"npm run copy\" \"npm run compile\"",
35 | "build:watch": "concurrently \"npm run copy:watch\" \"npm run compile:watch\"",
36 | "pretest": "npm run build",
37 | "test": "intern",
38 | "serve": "lite-server",
39 | "start": "concurrently \"npm run build:watch\" \"npm run serve\""
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/angular-example/systemjs-angular-loader.ts:
--------------------------------------------------------------------------------
1 | const templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
2 | const stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
3 | const stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
4 |
5 | function commonPath(path1: string[], path2: string[]) {
6 | const length = Math.min(path1.length, path2.length);
7 |
8 | let position: number;
9 |
10 | for (position = 0; position < length; position++) {
11 | if (path1[position] !== path2[position]) {
12 | position--;
13 | break;
14 | }
15 | }
16 |
17 | return path1.slice(0, position + 1);
18 | }
19 |
20 | function relative(from: string[], to: string[]) {
21 | const common = commonPath(from, to);
22 |
23 | to = to.slice(common.length);
24 | if (from.length === common.length) {
25 | return to;
26 | }
27 |
28 | return [...from.slice(common.length).map(_ => '..'), ...to];
29 | }
30 |
31 | const rootUrlParts = (() => {
32 | const url = document.createElement('a');
33 | url.href = document.baseURI;
34 | const parts = url.pathname.split('/');
35 | parts.pop();
36 | return parts;
37 | })();
38 |
39 | export function translate(load: { source: string; address: string; }) {
40 | if (load.source.indexOf('moduleId') !== -1) {
41 | return load;
42 | }
43 |
44 | const url = document.createElement('a');
45 | url.href = load.address;
46 | const urlParts = url.pathname.split('/');
47 | urlParts.pop();
48 |
49 | const baseHref = document.createElement('a');
50 | baseHref.href = this.baseURL;
51 | const baseHrefParts = baseHref.pathname.split('/');
52 |
53 | const relPath = relative(rootUrlParts, baseHrefParts).join('/');
54 |
55 | let basePath = urlParts.join('/');
56 | basePath = basePath.replace(baseHref.pathname, '');
57 | basePath = `${relPath}${basePath}`;
58 |
59 | load.source = load.source
60 | .replace(templateUrlRegex, function(_, __, resolvedUrl){
61 | if (resolvedUrl.startsWith('.')) {
62 | resolvedUrl = basePath + resolvedUrl.substr(1);
63 | }
64 |
65 | return `templateUrl: "${resolvedUrl}"`;
66 | })
67 | .replace(stylesRegex, function(_, relativeUrls) {
68 | const urls = [];
69 | let match: RegExpExecArray;
70 |
71 | while ((match = stringRegex.exec(relativeUrls)) !== null) {
72 | if (match[2].startsWith('.')) {
73 | urls.push(`"${basePath}${match[2].substr(1)}"`);
74 | } else {
75 | urls.push(`"${match[2]}"`);
76 | }
77 | }
78 |
79 | return `styleUrls: [${urls.join(', ')}]`;
80 | });
81 |
82 | return load;
83 | }
84 |
--------------------------------------------------------------------------------
/angular-example/systemjs.config.ts:
--------------------------------------------------------------------------------
1 | SystemJS.config({
2 | map: {
3 | 'rxjs': 'node_modules/rxjs',
4 | '@angular': 'node_modules/@angular',
5 | 'app': 'dist/app',
6 | 'tslib': 'node_modules/tslib/tslib.js'
7 | },
8 | // packages tells the System loader how to load when no filename and/or no extension
9 | packages: {
10 | '@angular/common': { main: 'bundles/common.umd.js' },
11 | '@angular/common/http': { main: '../bundles/common-http.umd.js' },
12 | '@angular/core': { main: 'bundles/core.umd.js' },
13 | '@angular/compiler': { main: 'bundles/compiler.umd.js' },
14 | '@angular/platform-browser': { main: 'bundles/platform-browser.umd.js' },
15 | '@angular/platform-browser-dynamic': { main: 'bundles/platform-browser-dynamic.umd.js' },
16 | '@angular/router': { main: 'bundles/router.umd.js' },
17 | '@angular/forms': { main: 'bundles/forms.umd.js' },
18 | app: {
19 | main: 'bootstrap.js',
20 | defaultExtension: 'js',
21 | meta: {
22 | './*.js': {
23 | loader: 'dist/systemjs-angular-loader.js'
24 | }
25 | }
26 | },
27 | rxjs: {
28 | defaultExtension: 'js'
29 | }
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/angular-example/tests/app.ts:
--------------------------------------------------------------------------------
1 | const { describe, it, beforeEach } = intern.getInterface('bdd');
2 | const { expect } = intern.getPlugin('chai');
3 | import { stub, SinonStub, SinonSpyCall } from 'sinon';
4 |
5 | import { ComponentFixture, TestBed } from '@angular/core/testing';
6 | import { By } from '@angular/platform-browser';
7 | import { DebugElement } from '@angular/core';
8 |
9 | import { TodoApp } from 'app/app';
10 | import { FormsModule } from '@angular/forms';
11 | import { TodoService, Todo } from 'app/services/store';
12 |
13 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
14 |
15 | describe('TodoApp', () => {
16 | let component: TodoApp;
17 | let fixture: ComponentFixture;
18 | let service: {
19 | get: SinonStub;
20 | add: SinonStub;
21 | };
22 | let objects: Todo[];
23 | let de: DebugElement;
24 |
25 | beforeEach(async () => {
26 | objects = [
27 | new Todo({ id: 1, title: 'one' }),
28 | new Todo({ id: 2, title: 'two' }),
29 | new Todo({ id: 3, title: 'three' })
30 | ];
31 |
32 | const mockService = {
33 | get: stub().callsFake(() => new BehaviorSubject(objects)),
34 | add: stub().callsFake((newTodo) => {
35 | const newObjects = [...objects];
36 | newObjects.push(newTodo);
37 | return new BehaviorSubject(newObjects);
38 | })
39 | };
40 |
41 | await TestBed.configureTestingModule({
42 | imports: [ FormsModule ],
43 | declarations: [
44 | TodoApp
45 | ],
46 | providers: [
47 | { provide: TodoService, useValue: mockService }
48 | ]
49 | })
50 | .compileComponents()
51 | ;
52 |
53 | fixture = TestBed.createComponent(TodoApp);
54 | component = fixture.componentInstance;
55 | de = fixture.debugElement;
56 | service = TestBed.get(TodoService);
57 | });
58 |
59 | it('should create an app', () => {
60 | expect(component).to.be.instanceOf(TodoApp);
61 | });
62 |
63 | it('should call TodoService#get() on init', () => {
64 | fixture.detectChanges();
65 |
66 | expect(service.get.called).to.be.true;
67 | });
68 |
69 | it('should call TodoService#add() when adding a todo', async () => {
70 | fixture.detectChanges();
71 |
72 | const input = de.query(By.css('.new-todo'));
73 | input.triggerEventHandler('input', {
74 | target: {
75 | value: 'stuff'
76 | }
77 | });
78 | input.triggerEventHandler('keyup.enter', null);
79 |
80 | await fixture.whenStable();
81 | fixture.detectChanges();
82 |
83 | expect(service.add.calledOnce).to.be.true;
84 | expect(service.add.calledWith('stuff')).to.be.true;
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/angular-example/tests/functional/Todo.ts:
--------------------------------------------------------------------------------
1 | const { describe, before, it } = intern.getInterface('bdd');
2 | const { expect } = intern.getPlugin('chai');
3 | import keys from '@theintern/leadfoot/keys';
4 |
5 | describe('functional/Todo', () => {
6 | before(async ({ remote }) => {
7 | await remote.get('index.html');
8 | await remote.setFindTimeout(5000);
9 | await remote.findDisplayedByCssSelector('todo-app');
10 | });
11 |
12 | it('should submit form', async ({ remote }) => {
13 |
14 | const input = remote.findByCssSelector('input.new-todo');
15 | await input.click();
16 | await input.type('Task 1');
17 | await input.type(keys.ENTER);
18 | await input.type('Task 2');
19 | await input.type(keys.ENTER);
20 | await input.type('Task 3');
21 | await input.type(keys.ENTER);
22 |
23 | const todos = await remote.findAllByCssSelector('.todo-list > li');
24 | expect(todos).to.have.lengthOf(3);
25 |
26 | expect(await todos[0].getVisibleText()).to.equal('Task 1');
27 | expect(await todos[1].getVisibleText()).to.equal('Task 2');
28 | expect(await todos[2].getVisibleText()).to.equal('Task 3');
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/angular-example/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/angular-example/tests/intern-angular-shim.ts:
--------------------------------------------------------------------------------
1 | interface AngularShimConfig {
2 | appConfig?: string | SystemJSLoader.Config;
3 | testConfig?: string | SystemJSLoader.Config;
4 | require?: string | string[];
5 | }
6 |
7 | intern.registerPlugin('angular-shim', async (config: AngularShimConfig) => {
8 | async function configureLoader(config: string | SystemJSLoader.Config) {
9 | if (config) {
10 | if (typeof config === "string") {
11 | await intern.loadScript(config);
12 | } else {
13 | SystemJS.config(config);
14 | }
15 | }
16 | }
17 |
18 | async function initTestEnv() {
19 | const { TestBed } = await SystemJS.import('@angular/core/testing');
20 | const { BrowserDynamicTestingModule, platformBrowserDynamicTesting } = await SystemJS.import('@angular/platform-browser-dynamic/testing');
21 |
22 | TestBed.initTestEnvironment(
23 | BrowserDynamicTestingModule,
24 | platformBrowserDynamicTesting()
25 | );
26 |
27 | intern.on('suiteAdd', suite => {
28 | suite['afterEach'] = () => {
29 | TestBed.resetTestingModule();
30 | };
31 | });
32 | }
33 |
34 | await configureLoader(config.appConfig);
35 | await configureLoader(config.testConfig);
36 |
37 | if (config.require && config.require.length) {
38 | await intern.loadScript(config.require);
39 | }
40 |
41 | await initTestEnv();
42 | });
43 |
--------------------------------------------------------------------------------
/angular-example/tests/services/store.ts:
--------------------------------------------------------------------------------
1 | const { describe, it, beforeEach } = intern.getInterface('bdd');
2 | const { expect } = intern.getPlugin('chai');
3 |
4 | import { TodoService, TodoObject, Todo } from 'app/services/store';
5 | import { LocalStorageService } from 'app/services/localStorage';
6 |
7 | import 'rxjs/add/operator/toPromise';
8 |
9 | let objects: Todo[];
10 | class MockLocalStorageService {
11 | get() {
12 | return objects;
13 | }
14 |
15 | set(value: any[]) {
16 | objects = value;
17 | }
18 |
19 | clear() {
20 | objects = [];
21 | }
22 | }
23 |
24 | describe('TodoStore', () => {
25 | let service: TodoService;
26 | let localStorage: MockLocalStorageService;
27 |
28 | beforeEach(() => {
29 | objects = [
30 | new Todo({ id: 1, title: 'one' }),
31 | new Todo({ id: 2, title: 'two' }),
32 | new Todo({ id: 3, title: 'three' })
33 | ];
34 | localStorage = new MockLocalStorageService();
35 | service = new TodoService(localStorage as any);
36 | });
37 |
38 | it('should create a service', () => {
39 | expect(service).not.to.be.null;
40 | });
41 |
42 | it('should return an Observable of Todos', async () => {
43 | const result = await service.get().toPromise();
44 | expect(result).to.deep.equal(objects);
45 | expect(result).not.to.equal(objects);
46 | });
47 |
48 | it('should add a new Todo', async () => {
49 | const result = await service.add('four').toPromise();
50 | expect(result).to.have.lengthOf(4);
51 | expect(result[3].title).to.equal('four');
52 | });
53 |
54 | it('should update a Todo', async () => {
55 | const todo = objects[1];
56 | todo.title = 'two edited';
57 | const result = await service.update(todo).toPromise();
58 | expect(result).to.have.lengthOf(3);
59 | expect(result[1]).to.deep.equal(todo);
60 | expect(result[1]).not.to.equal(todo);
61 | });
62 |
63 | it('should set new Todos', async () => {
64 | const todos = [
65 | new Todo({ title: 'foo' }),
66 | new Todo({ title: 'bar' })
67 | ];
68 | const result = await service.set(todos).toPromise();
69 | expect(result).to.have.lengthOf(2);
70 | expect(result).to.be.deep.equal(todos);
71 | expect(result).not.to.equal(todos);
72 | });
73 |
74 | it('should delete a Todo', async () => {
75 | const original = [...objects];
76 | const result = await service.delete(objects[1]).toPromise();
77 | expect(result).to.have.lengthOf(2);
78 | expect(result[0]).to.deep.equal(original[0]);
79 | expect(result[1]).to.deep.equal(original[2]);
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/angular-example/tests/systemjs-istanbuljs.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This module hooks the SystemJS translate function to apply code coverage instrumentation.
3 | */
4 |
5 | import { createInstrumenter } from 'istanbul-lib-instrument';
6 | import { readFileSync } from 'fs';
7 | import { parse } from 'url';
8 | import { dirname, normalize, join } from 'path';
9 |
10 | const istanbulGlobal = '__coverage__';
11 | const sourceMapRegex = /(?:\/{2}[#@]{1,2}|\/\*)\s+sourceMappingURL\s*=\s*(data:(?:[^;]+;)+base64,)?(\S+)/;
12 |
13 | const instrumenter = createInstrumenter({
14 | coverageVariable: istanbulGlobal
15 | });
16 |
17 | const esInstrumenter = createInstrumenter({
18 | coverageVariable: istanbulGlobal,
19 | esModules: true
20 | });
21 |
22 | export function translate(this: SystemJSLoader.System, load: any) {
23 | const source = load.source;
24 | const meta = load.metadata;
25 |
26 | if (
27 | meta.format === 'json' ||
28 | meta.format === 'defined' ||
29 | meta.loaderModule && meta.loaderModule.build === false
30 | ) {
31 | return source;
32 | }
33 |
34 | const match = sourceMapRegex.exec(load.source);
35 | let sourceMap: any;
36 | if (match) {
37 | const sourcePath = parse(load.address).pathname!;
38 | const mapPath = match[2];
39 | const mapFile = normalize(join(dirname(sourcePath), mapPath));
40 | try {
41 | sourceMap = JSON.parse(readFileSync(mapFile, { encoding: 'utf8' }));
42 | }
43 | catch (_error) {
44 | console.warn('Unable to load source map at ' + mapFile);
45 | }
46 | }
47 |
48 | const baseURL = (meta.istanbul && this.resolveSync(meta.istanbul.baseURL)) || this.baseURL!;
49 | const name = normalize(load.address.slice(baseURL.length));
50 |
51 | try {
52 | if (meta.format === 'esm') {
53 | return esInstrumenter.instrumentSync(source, name, sourceMap);
54 | }
55 | else {
56 | return instrumenter.instrumentSync(source, name, sourceMap);
57 | }
58 | }
59 | catch (error) {
60 | const message = `Unable to add coverage instrumentation to "${name}".\n\t`;
61 | const newErr: SystemJSError = new Error(`${message}${error.message}`);
62 | newErr.stack = `${message}${error.stack}`;
63 | newErr.originalErr = error.originalErr || error;
64 | throw newErr;
65 | }
66 | }
67 |
68 | interface SystemJSError extends Error {
69 | originalErr?: Error;
70 | }
71 |
--------------------------------------------------------------------------------
/angular-example/tests/systemjs.config.ts:
--------------------------------------------------------------------------------
1 | SystemJS.config({
2 | map: {
3 | 'sinon': 'node_modules/sinon/pkg/sinon.js'
4 | },
5 | packages: {
6 | '@angular/animations/browser/testing': {
7 | main: '../../bundles/animations-browser-testing.umd.js'
8 | },
9 | '@angular/core/testing': {
10 | main: '../bundles/core-testing.umd.js'
11 | },
12 | '@angular/common/testing': {
13 | main: '../bundles/common-testing.umd.js'
14 | },
15 | '@angular/common/http/testing': {
16 | main: '../../bundles/common-http-testing.umd.js'
17 | },
18 | '@angular/compiler/testing': {
19 | main: '../bundles/compiler-testing.umd.js'
20 | },
21 | '@angular/platform-browser/testing': {
22 | main: '../bundles/platform-browser-testing.umd.js'
23 | },
24 | '@angular/platform-browser-dynamic/testing': {
25 | main: '../bundles/platform-browser-dynamic-testing.umd.js'
26 | },
27 | '@angular/router/testing': {
28 | main: '../bundles/router-testing.umd.js'
29 | },
30 | '@angular/forms/testing': {
31 | main: '../bundles/forms-testing.umd.js'
32 | }
33 | }
34 | });
35 |
--------------------------------------------------------------------------------
/angular-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "es5",
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "sourceMap": true,
8 | "outDir": "dist",
9 | "paths": {
10 | "app/*": [
11 | "./app/*"
12 | ]
13 | },
14 | "importHelpers": true,
15 | "emitDecoratorMetadata": true,
16 | "experimentalDecorators": true,
17 | "removeComments": false,
18 | "noImplicitAny": true,
19 | "lib": [
20 | "es5",
21 | "es2015.core",
22 | "es2015.symbol.wellknown",
23 | "es2015.collection",
24 | "dom"
25 | ],
26 | "types": [
27 | "intern",
28 | "systemjs"
29 | ],
30 | "typeRoots": [
31 | "./node_modules/@types",
32 | "./node_modules/intern/types"
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/angularjs-example/README.md:
--------------------------------------------------------------------------------
1 | angular-1x-example
2 | =============
3 |
4 | ## Setup
5 |
6 | 1. Install the JRE or JDK
7 | This demo runs with local Selenium, which Intern will automatically install.
8 |
9 | 2. Install intern command line interface
10 |
11 | ```
12 | npm install -g intern-cli
13 | ```
14 |
15 | 3. Install node modules and intern
16 |
17 | ```
18 | npm install
19 | ```
20 |
21 | ## Running tests
22 |
23 | * **Local browser tests**
24 |
25 | ```
26 | intern serve
27 | ```
28 |
29 | Navigate to `http://localhost:9000/node_modules/intern/client.html?config=tests/intern.js`.
30 |
31 | * **Remote node / browser tests**
32 |
33 | ```
34 | intern run --webdriver
35 | ```
36 |
--------------------------------------------------------------------------------
/angularjs-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | AngularJS • TodoMVC
7 |
8 |
9 |
10 |
11 |
52 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/angularjs-example/js/app.js:
--------------------------------------------------------------------------------
1 | /*global angular */
2 | /*jshint unused:false */
3 | 'use strict';
4 |
5 | /**
6 | * The main TodoMVC app module
7 | *
8 | * @type {angular.Module}
9 | */
10 | var todomvc = angular.module('todomvc', []);
11 |
--------------------------------------------------------------------------------
/angularjs-example/js/controllers/todoCtrl.js:
--------------------------------------------------------------------------------
1 | /*global todomvc, angular */
2 | 'use strict';
3 |
4 | /**
5 | * The main controller for the app. The controller:
6 | * - retrieves and persists the model via the todoStorage service
7 | * - exposes the model to the template and provides event handlers
8 | */
9 | todomvc.controller('TodoCtrl', function TodoCtrl($scope, $location, todoStorage, filterFilter) {
10 | var todos = $scope.todos = todoStorage.get();
11 |
12 | $scope.newTodo = '';
13 | $scope.editedTodo = null;
14 |
15 | $scope.$watch('todos', function (newValue, oldValue) {
16 | $scope.remainingCount = filterFilter(todos, { completed: false }).length;
17 | $scope.completedCount = todos.length - $scope.remainingCount;
18 | $scope.allChecked = !$scope.remainingCount;
19 | if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
20 | todoStorage.put(todos);
21 | }
22 | }, true);
23 |
24 | if ($location.path() === '') {
25 | $location.path('/');
26 | }
27 |
28 | $scope.location = $location;
29 |
30 | $scope.$watch('location.path()', function (path) {
31 | $scope.statusFilter = (path === '/active') ?
32 | { completed: false } : (path === '/completed') ?
33 | { completed: true } : null;
34 | });
35 |
36 | $scope.addTodo = function () {
37 | var newTodo = $scope.newTodo.trim();
38 | if (!newTodo.length) {
39 | return;
40 | }
41 |
42 | todos.push({
43 | title: newTodo,
44 | completed: false
45 | });
46 |
47 | $scope.newTodo = '';
48 | };
49 |
50 | $scope.editTodo = function (todo) {
51 | $scope.editedTodo = todo;
52 | // Clone the original todo to restore it on demand.
53 | $scope.originalTodo = angular.extend({}, todo);
54 | };
55 |
56 | $scope.doneEditing = function (todo) {
57 | $scope.editedTodo = null;
58 | todo.title = todo.title.trim();
59 |
60 | if (!todo.title) {
61 | $scope.removeTodo(todo);
62 | }
63 | };
64 |
65 | $scope.revertEditing = function (todo) {
66 | todos[todos.indexOf(todo)] = $scope.originalTodo;
67 | $scope.doneEditing($scope.originalTodo);
68 | };
69 |
70 | $scope.removeTodo = function (todo) {
71 | todos.splice(todos.indexOf(todo), 1);
72 | };
73 |
74 | $scope.clearCompletedTodos = function () {
75 | $scope.todos = todos = todos.filter(function (val) {
76 | return !val.completed;
77 | });
78 | };
79 |
80 | $scope.markAll = function (completed) {
81 | todos.forEach(function (todo) {
82 | todo.completed = completed;
83 | });
84 | };
85 | });
--------------------------------------------------------------------------------
/angularjs-example/js/directives/todoBlur.js:
--------------------------------------------------------------------------------
1 | /*global todomvc */
2 | 'use strict';
3 |
4 | /**
5 | * Directive that executes an expression when the element it is applied to loses focus
6 | */
7 | todomvc.directive('todoBlur', function () {
8 | return function (scope, elem, attrs) {
9 | elem.bind('blur', function () {
10 | scope.$apply(attrs.todoBlur);
11 | });
12 | };
13 | });
14 |
--------------------------------------------------------------------------------
/angularjs-example/js/directives/todoEscape.js:
--------------------------------------------------------------------------------
1 | /*global todomvc */
2 | 'use strict';
3 |
4 | /**
5 | * Directive that executes an expression when the element it is applied to gets
6 | * an `escape` keydown event.
7 | */
8 | todomvc.directive('todoEscape', function () {
9 | var ESCAPE_KEY = 27;
10 | return function (scope, elem, attrs) {
11 | elem.bind('keydown', function (event) {
12 | if (event.keyCode === ESCAPE_KEY) {
13 | scope.$apply(attrs.todoEscape);
14 | }
15 | });
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/angularjs-example/js/directives/todoFocus.js:
--------------------------------------------------------------------------------
1 | /*global todomvc */
2 | 'use strict';
3 |
4 | /**
5 | * Directive that places focus on the element it is applied to when the expression it binds to evaluates to true
6 | */
7 | todomvc.directive('todoFocus', function todoFocus($timeout) {
8 | return function (scope, elem, attrs) {
9 | scope.$watch(attrs.todoFocus, function (newVal) {
10 | if (newVal) {
11 | $timeout(function () {
12 | elem[0].focus();
13 | }, 0, false);
14 | }
15 | });
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/angularjs-example/js/services/todoStorage.js:
--------------------------------------------------------------------------------
1 | /*global todomvc */
2 | 'use strict';
3 |
4 | /**
5 | * Services that persists and retrieves TODOs from localStorage
6 | */
7 | todomvc.factory('todoStorage', function () {
8 | var STORAGE_ID = 'todos-angularjs';
9 |
10 | return {
11 | get: function () {
12 | return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
13 | },
14 |
15 | put: function (todos) {
16 | localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
17 | }
18 | };
19 | });
--------------------------------------------------------------------------------
/angularjs-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angularjs-intern-example",
3 | "version": "0.2.0",
4 | "description": "An example showing a typical Angular 1.x application tested with Intern",
5 | "devDependencies": {
6 | "angular-mocks": "~1.2.18",
7 | "intern": "~3.4.2"
8 | },
9 | "dependencies": {
10 | "angular": "~1.8.0",
11 | "todomvc-common": "~0.1.4"
12 | },
13 | "scripts": {
14 | "test": "intern-runner config=tests/intern"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/angularjs-example/tests/all.js:
--------------------------------------------------------------------------------
1 | define([
2 | './directives/todoBlurFocus',
3 | './controllers/todoCtrl'
4 | ], function () {});
--------------------------------------------------------------------------------
/angularjs-example/tests/directives/todoBlurFocus.js:
--------------------------------------------------------------------------------
1 | /*global angular*/
2 |
3 | define([
4 | 'intern/chai!expect',
5 | 'intern!bdd',
6 | 'intern/order!angular/angular',
7 | 'intern/order!angular-mocks/angular-mocks',
8 | 'intern/order!todo/app',
9 | 'intern/order!todo/directives/todoBlur',
10 | 'intern/order!todo/directives/todoFocus'
11 | ], function (expect, bdd) {
12 |
13 | function inject (fn) {
14 | return function() {
15 | angular.injector(['ng', 'ngMock', 'todomvc']).invoke(fn);
16 | }
17 | }
18 |
19 | bdd.describe('todoBlur directive', function () {
20 | var scope, compile, browser;
21 |
22 | bdd.beforeEach(inject(function ($rootScope, $compile, $browser) {
23 | scope = $rootScope.$new();
24 | scope.mock = {
25 | called: false,
26 | call: function () { this.called = true; }
27 | };
28 | compile = $compile;
29 | browser = $browser;
30 | }));
31 |
32 | bdd.it('should $apply on blur', function () {
33 | var el = angular.element('');
34 | compile(el)(scope);
35 |
36 | el.triggerHandler('blur');
37 | scope.$digest();
38 |
39 | expect(scope.mock.called).to.be.true;
40 | });
41 |
42 | bdd.it('should focus on truthy expression', function () {
43 | scope.focus = false;
44 |
45 | var el = angular.element('');
46 | compile(el)(scope);
47 |
48 | expect(browser.deferredFns).to.have.length(0);
49 |
50 | scope.$apply(function () {
51 | scope.focus = true;
52 | });
53 |
54 | expect(browser.deferredFns).to.have.length(1);
55 | });
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/angularjs-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert',
4 | 'require'
5 | ], function (registerSuite, assert, require) {
6 | var url = '../../index.html';
7 |
8 | registerSuite({
9 | name: 'Todo (functional)',
10 |
11 | 'submit form': function () {
12 | return this.remote
13 | .get(require.toUrl(url))
14 | .findById('new-todo')
15 | .click()
16 | .pressKeys('Task 1')
17 | .pressKeys('\n')
18 | .pressKeys('Task 2')
19 | .pressKeys('\n')
20 | .pressKeys('Task 3')
21 | .getProperty('value')
22 | .then(function (val) {
23 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
24 | });
25 | }
26 | });
27 | });
--------------------------------------------------------------------------------
/angularjs-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | // Learn more about configuring this file at .
2 | // These default settings work OK for most people. The options that *must* be changed below are the
3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites.
4 | define({
5 | // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the
6 | // specified browser environments in the `environments` array below as well. See
7 | // for links to the different capabilities options for
8 | // different services.
9 | //
10 | // Note that the `build` capability will be filled in with the current commit ID or build tag from the CI
11 | // environment automatically
12 | capabilities: {},
13 |
14 | // Browsers to run integration testing against. Options that will be permutated are browserName, version, platform,
15 | // and platformVersion; any other capabilities options specified for an environment will be copied as-is. Note that
16 | // browser and platform names, and version number formats, may differ between cloud testing systems.
17 | environments: [ { browserName: 'chrome' } ],
18 |
19 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service
20 | maxConcurrency: 3,
21 |
22 | // Name of the tunnel class to use for WebDriver tests.
23 | // See for built-in options
24 | tunnel: 'SeleniumTunnel',
25 |
26 | // Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use
27 | // can be used here.
28 | // If you want to use a different loader than the default loader, see
29 | // for more information.
30 | loaderOptions: {
31 | // Packages that should be registered with the loader in each testing environment
32 | packages: [
33 | { name: 'todo', location: 'js' },
34 | { name: 'angular', location: 'node_modules/angular' },
35 | { name: 'angular-mocks', location: 'node_modules/angular-mocks' }
36 | ]
37 | },
38 |
39 | // Unit test suite(s) to run in each browser
40 | suites: [ 'tests/all' ],
41 |
42 | // Functional test suite(s) to execute against each browser once unit tests are completed
43 | functionalSuites: [ 'tests/functional/Todo' ],
44 |
45 | // A regular expression matching URLs to files that should not be included in code coverage analysis. Set to `true`
46 | // to completely disable code coverage.
47 | excludeInstrumentation: /^(?:tests|node_modules)\//
48 | });
49 |
--------------------------------------------------------------------------------
/backbone-example/README.md:
--------------------------------------------------------------------------------
1 | backbone-example
2 | =============
3 |
4 | ## Setup
5 |
6 | 1. Install the JRE or JDK
7 | This demo runs with local Selenium, which Intern will automatically install.
8 |
9 | 2. Install intern command line interface
10 |
11 | ```
12 | npm install -g intern-cli
13 | ```
14 |
15 | 3. Install node modules and intern
16 |
17 | ```
18 | npm install
19 | ```
20 |
21 | ## Running tests
22 |
23 | * **Local browser tests**
24 |
25 | ```
26 | intern serve
27 | ```
28 |
29 | Navigate to `http://localhost:9000/node_modules/intern/client.html?config=tests/intern.js`.
30 |
31 | * **Remote node / browser tests**
32 |
33 | ```
34 | intern run --webdriver
35 | ```
36 |
--------------------------------------------------------------------------------
/backbone-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Backbone.js • TodoMVC
7 |
8 |
9 |
10 |
22 |
27 |
35 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/backbone-example/js/app.js:
--------------------------------------------------------------------------------
1 | /*global $ */
2 | /*jshint unused:false */
3 | var app = app || {};
4 | var ENTER_KEY = 13;
5 |
6 | $(function () {
7 | 'use strict';
8 |
9 | // kick things off by creating the `App`
10 | new app.AppView();
11 | });
12 |
--------------------------------------------------------------------------------
/backbone-example/js/collections/todos.js:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | var app = app || {};
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | // Todo Collection
8 | // ---------------
9 |
10 | // The collection of todos is backed by *localStorage* instead of a remote
11 | // server.
12 | var Todos = Backbone.Collection.extend({
13 | // Reference to this collection's model.
14 | model: app.Todo,
15 |
16 | // Save all of the todo items under the `"todos"` namespace.
17 | localStorage: new Backbone.LocalStorage('todos-backbone'),
18 |
19 | // Filter down the list of all todo items that are finished.
20 | completed: function () {
21 | return this.filter(function (todo) {
22 | return todo.get('completed');
23 | });
24 | },
25 |
26 | // Filter down the list to only todo items that are still not finished.
27 | remaining: function () {
28 | return this.without.apply(this, this.completed());
29 | },
30 |
31 | // We keep the Todos in sequential order, despite being saved by unordered
32 | // GUID in the database. This generates the next order number for new items.
33 | nextOrder: function () {
34 | if (!this.length) {
35 | return 1;
36 | }
37 | return this.last().get('order') + 1;
38 | },
39 |
40 | // Todos are sorted by their original insertion order.
41 | comparator: function (todo) {
42 | return todo.get('order');
43 | }
44 | });
45 |
46 | // Create our global collection of **Todos**.
47 | app.todos = new Todos();
48 | })();
49 |
--------------------------------------------------------------------------------
/backbone-example/js/models/todo.js:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | var app = app || {};
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | // Todo Model
8 | // ----------
9 |
10 | // Our basic **Todo** model has `title`, `order`, and `completed` attributes.
11 | app.Todo = Backbone.Model.extend({
12 | // Default attributes for the todo
13 | // and ensure that each todo created has `title` and `completed` keys.
14 | defaults: {
15 | title: '',
16 | completed: false
17 | },
18 |
19 | // Toggle the `completed` state of this todo item.
20 | toggle: function () {
21 | this.save({
22 | completed: !this.get('completed')
23 | });
24 | }
25 | });
26 | })();
27 |
--------------------------------------------------------------------------------
/backbone-example/js/routers/router.js:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | var app = app || {};
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | // Todo Router
8 | // ----------
9 | var TodoRouter = Backbone.Router.extend({
10 | routes: {
11 | '*filter': 'setFilter'
12 | },
13 |
14 | setFilter: function (param) {
15 | // Set the current filter to be used
16 | app.TodoFilter = param || '';
17 |
18 | // Trigger a collection filter event, causing hiding/unhiding
19 | // of Todo view items
20 | app.todos.trigger('filter');
21 | }
22 | });
23 |
24 | app.TodoRouter = new TodoRouter();
25 | Backbone.history.start();
26 | })();
27 |
--------------------------------------------------------------------------------
/backbone-example/js/views/app-view.js:
--------------------------------------------------------------------------------
1 | /*global Backbone, jQuery, _, ENTER_KEY */
2 | var app = app || {};
3 |
4 | (function ($) {
5 | 'use strict';
6 |
7 | // The Application
8 | // ---------------
9 |
10 | // Our overall **AppView** is the top-level piece of UI.
11 | app.AppView = Backbone.View.extend({
12 |
13 | // Instead of generating a new element, bind to the existing skeleton of
14 | // the App already present in the HTML.
15 | el: '#todoapp',
16 |
17 | // Our template for the line of statistics at the bottom of the app.
18 | statsTemplate: _.template($('#stats-template').html()),
19 |
20 | // Delegated events for creating new items, and clearing completed ones.
21 | events: {
22 | 'keypress #new-todo': 'createOnEnter',
23 | 'click #clear-completed': 'clearCompleted',
24 | 'click #toggle-all': 'toggleAllComplete'
25 | },
26 |
27 | // At initialization we bind to the relevant events on the `Todos`
28 | // collection, when items are added or changed. Kick things off by
29 | // loading any preexisting todos that might be saved in *localStorage*.
30 | initialize: function () {
31 | this.allCheckbox = this.$('#toggle-all')[0];
32 | this.$input = this.$('#new-todo');
33 | this.$footer = this.$('#footer');
34 | this.$main = this.$('#main');
35 |
36 | this.listenTo(app.todos, 'add', this.addOne);
37 | this.listenTo(app.todos, 'reset', this.addAll);
38 | this.listenTo(app.todos, 'change:completed', this.filterOne);
39 | this.listenTo(app.todos, 'filter', this.filterAll);
40 | this.listenTo(app.todos, 'all', this.render);
41 |
42 | app.todos.fetch();
43 | },
44 |
45 | // Re-rendering the App just means refreshing the statistics -- the rest
46 | // of the app doesn't change.
47 | render: function () {
48 | var completed = app.todos.completed().length;
49 | var remaining = app.todos.remaining().length;
50 |
51 | if (app.todos.length) {
52 | this.$main.show();
53 | this.$footer.show();
54 |
55 | this.$footer.html(this.statsTemplate({
56 | completed: completed,
57 | remaining: remaining
58 | }));
59 |
60 | this.$('#filters li a')
61 | .removeClass('selected')
62 | .filter('[href="#/' + (app.TodoFilter || '') + '"]')
63 | .addClass('selected');
64 | } else {
65 | this.$main.hide();
66 | this.$footer.hide();
67 | }
68 |
69 | this.allCheckbox.checked = !remaining;
70 | },
71 |
72 | // Add a single todo item to the list by creating a view for it, and
73 | // appending its element to the ``.
74 | addOne: function (todo) {
75 | var view = new app.TodoView({ model: todo });
76 | $('#todo-list').append(view.render().el);
77 | },
78 |
79 | // Add all items in the **Todos** collection at once.
80 | addAll: function () {
81 | this.$('#todo-list').html('');
82 | app.todos.each(this.addOne, this);
83 | },
84 |
85 | filterOne: function (todo) {
86 | todo.trigger('visible');
87 | },
88 |
89 | filterAll: function () {
90 | app.todos.each(this.filterOne, this);
91 | },
92 |
93 | // Generate the attributes for a new Todo item.
94 | newAttributes: function () {
95 | return {
96 | title: this.$input.val().trim(),
97 | order: app.todos.nextOrder(),
98 | completed: false
99 | };
100 | },
101 |
102 | // If you hit return in the main input field, create new **Todo** model,
103 | // persisting it to *localStorage*.
104 | createOnEnter: function (e) {
105 | if (e.which !== ENTER_KEY || !this.$input.val().trim()) {
106 | return;
107 | }
108 |
109 | app.todos.create(this.newAttributes());
110 | this.$input.val('');
111 | },
112 |
113 | // Clear all completed todo items, destroying their models.
114 | clearCompleted: function () {
115 | _.invoke(app.todos.completed(), 'destroy');
116 | return false;
117 | },
118 |
119 | toggleAllComplete: function () {
120 | var completed = this.allCheckbox.checked;
121 |
122 | app.todos.each(function (todo) {
123 | todo.save({
124 | 'completed': completed
125 | });
126 | });
127 | }
128 | });
129 | })(jQuery);
130 |
--------------------------------------------------------------------------------
/backbone-example/js/views/todo-view.js:
--------------------------------------------------------------------------------
1 | /*global Backbone, jQuery, _, ENTER_KEY */
2 | var app = app || {};
3 |
4 | (function ($) {
5 | 'use strict';
6 |
7 | // Todo Item View
8 | // --------------
9 |
10 | // The DOM element for a todo item...
11 | app.TodoView = Backbone.View.extend({
12 | //... is a list tag.
13 | tagName: 'li',
14 |
15 | // Cache the template function for a single item.
16 | template: _.template($('#item-template').html()),
17 |
18 | // The DOM events specific to an item.
19 | events: {
20 | 'click .toggle': 'toggleCompleted',
21 | 'dblclick label': 'edit',
22 | 'click .destroy': 'clear',
23 | 'keypress .edit': 'updateOnEnter',
24 | 'blur .edit': 'close'
25 | },
26 |
27 | // The TodoView listens for changes to its model, re-rendering. Since there's
28 | // a one-to-one correspondence between a **Todo** and a **TodoView** in this
29 | // app, we set a direct reference on the model for convenience.
30 | initialize: function () {
31 | this.listenTo(this.model, 'change', this.render);
32 | this.listenTo(this.model, 'destroy', this.remove);
33 | this.listenTo(this.model, 'visible', this.toggleVisible);
34 | },
35 |
36 | // Re-render the titles of the todo item.
37 | render: function () {
38 | this.$el.html(this.template(this.model.toJSON()));
39 | this.$el.toggleClass('completed', this.model.get('completed'));
40 | this.toggleVisible();
41 | this.$input = this.$('.edit');
42 | return this;
43 | },
44 |
45 | toggleVisible: function () {
46 | this.$el.toggleClass('hidden', this.isHidden());
47 | },
48 |
49 | isHidden: function () {
50 | var isCompleted = this.model.get('completed');
51 | return (// hidden cases only
52 | (!isCompleted && app.TodoFilter === 'completed') ||
53 | (isCompleted && app.TodoFilter === 'active')
54 | );
55 | },
56 |
57 | // Toggle the `"completed"` state of the model.
58 | toggleCompleted: function () {
59 | this.model.toggle();
60 | },
61 |
62 | // Switch this view into `"editing"` mode, displaying the input field.
63 | edit: function () {
64 | this.$el.addClass('editing');
65 | this.$input.focus();
66 | },
67 |
68 | // Close the `"editing"` mode, saving changes to the todo.
69 | close: function () {
70 | var trimmedValue = this.$input.val().trim();
71 | this.$input.val(trimmedValue);
72 |
73 | if (trimmedValue) {
74 | this.model.save({ title: trimmedValue });
75 | } else {
76 | this.clear();
77 | }
78 |
79 | this.$el.removeClass('editing');
80 | },
81 |
82 | // If you hit `enter`, we're through editing the item.
83 | updateOnEnter: function (e) {
84 | if (e.which === ENTER_KEY) {
85 | this.close();
86 | }
87 | },
88 |
89 | // Remove the item, destroy the model from *localStorage* and delete its view.
90 | clear: function () {
91 | this.model.destroy();
92 | }
93 | });
94 | })(jQuery);
95 |
--------------------------------------------------------------------------------
/backbone-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backbone-intern-example",
3 | "version": "0.2.0",
4 | "description": "An example showing a typical Backbone application tested with Intern",
5 | "devDependencies": {
6 | "intern": "~3.4.2"
7 | },
8 | "dependencies": {
9 | "backbone": "~1.0.0",
10 | "backbone.localstorage": "~1.1.0",
11 | "underscore": "~1.4.4",
12 | "jquery": "~3.5.1",
13 | "todomvc-common": "~0.1.4"
14 | },
15 | "scripts": {
16 | "test": "intern-runner config=tests/intern"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backbone-example/tests/all.js:
--------------------------------------------------------------------------------
1 | define([
2 | './models/todo'
3 | ], function () {});
--------------------------------------------------------------------------------
/backbone-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert',
4 | 'require'
5 | ], function (registerSuite, assert, require) {
6 | var url = '../../index.html';
7 |
8 | registerSuite({
9 | name: 'Todo (functional)',
10 |
11 | 'submit form': function () {
12 | return this.remote
13 | .get(require.toUrl(url))
14 | .findById('new-todo')
15 | .click()
16 | .pressKeys('Task 1')
17 | .pressKeys('\n')
18 | .pressKeys('Task 2')
19 | .pressKeys('\n')
20 | .pressKeys('Task 3')
21 | .getProperty('value')
22 | .then(function (val) {
23 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
24 | });
25 | }
26 | });
27 | });
--------------------------------------------------------------------------------
/backbone-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | // Learn more about configuring this file at .
2 | // These default settings work OK for most people. The options that *must* be changed below are the
3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites.
4 | define([ 'intern' ], function (intern) {
5 | var config = {
6 | // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the
7 | // specified browser environments in the `environments` array below as well. See
8 | // for links to the different capabilities options for
9 | // different services.
10 | //
11 | // Note that the `build` capability will be filled in with the current commit ID or build tag from the CI
12 | // environment automatically
13 | capabilities: {},
14 |
15 | // Browsers to run integration testing against. Options that will be permutated are browserName, version,
16 | // platform, and platformVersion; any other capabilities options specified for an environment will be copied
17 | // as-is. Note that browser and platform names, and version number formats, may differ between cloud testing
18 | // systems.
19 | environments: [ { browserName: 'chrome' } ],
20 |
21 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service
22 | maxConcurrency: 3,
23 |
24 | // Name of the tunnel class to use for WebDriver tests.
25 | // See for built-in options
26 | tunnel: 'SeleniumTunnel',
27 |
28 | // Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use
29 | // can be used here.
30 | // If you want to use a different loader than the default loader, see
31 | // for more information.
32 | loaderOptions: {
33 | // Packages that should be registered with the loader in each testing environment
34 | packages: [
35 | { name: 'todo', location: 'js' },
36 | { name: 'jquery', location: 'node_modules/jquery' },
37 | { name: 'underscore', location: 'node_modules/underscore' },
38 | { name: 'backbone', location: 'node_modules/backbone' },
39 | { name: 'backboneStorage', location: 'node_modules/backbone.localstorage' }
40 | ]
41 | },
42 |
43 | // Unit test suite(s) to run in each browser
44 | suites: [ 'tests/all' ],
45 |
46 | // Functional test suite(s) to execute against each browser once unit tests are completed
47 | functionalSuites: [ 'tests/functional/Todo' ],
48 |
49 | // A regular expression matching URLs to files that should not be included in code coverage analysis. Set to
50 | // `true` to completely disable code coverage.
51 | excludeInstrumentation: /^(?:tests|node_modules)\//
52 | };
53 |
54 | if (intern.mode === 'runner') {
55 | config.reporters = [ 'Runner', 'Lcovhtml' ];
56 | }
57 |
58 | return config;
59 | });
60 |
--------------------------------------------------------------------------------
/backbone-example/tests/models/todo.js:
--------------------------------------------------------------------------------
1 | /*global $:false, app:false */
2 |
3 | define([
4 | 'intern!object',
5 | 'intern/chai!assert',
6 | 'intern/order!jquery/jquery',
7 | 'intern/order!underscore/underscore',
8 | 'intern/order!backbone/backbone',
9 | 'intern/order!todo/models/todo'
10 | ], function (registerSuite, assert) {
11 | var todo,
12 | ajax;
13 |
14 | registerSuite({
15 | name: 'todo model',
16 |
17 | setup: function () {
18 | // Extend the Todo model only to add a urlRoot. This
19 | // property is required for a model to call save()
20 | // without throwing an error
21 | var Model = app.Todo.extend({
22 | urlRoot: 'mockUrlRoot'
23 | });
24 | todo = new Model();
25 |
26 | // Mock the jquery ajax method for now
27 | ajax = $.ajax;
28 | $.ajax = function () {};
29 | },
30 |
31 | teardown: function () {
32 | $.ajax = ajax;
33 | },
34 |
35 | defaults: function () {
36 | assert.isFalse(todo.get('completed'), 'A Todo model should default the completed property to false');
37 | assert.strictEqual(todo.get('title'), '', 'A Todo model should default the title property to an empty string');
38 | },
39 |
40 | toggle: function () {
41 | todo.toggle();
42 | assert.isTrue(todo.get('completed'), '', 'Completed property should switch to true after being toggled for the first time');
43 | todo.toggle();
44 | assert.isFalse(todo.get('completed'), '', 'Completed property should switch back to false after being toggled again');
45 | }
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/dojo-example/README.md:
--------------------------------------------------------------------------------
1 | dojo-example
2 | =============
3 |
4 | It is based on the [TodoMVC Dojo Example](http://todomvc.com/examples/dojo/).
5 |
6 | ## Setup
7 |
8 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
9 |
10 | 2. Install node modules
11 | ```
12 | $ npm install
13 | ```
14 |
15 | ## Running tests
16 |
17 | **Run unit and functional tests in Chrome**
18 |
19 | $ npm test
20 |
21 | **Run unit and functional tests in other browsers**
22 |
23 | $ npm test config=@firefox
24 | $ npm test config=@ie
25 |
26 | Note that the above commands all require that the browser be available on the test system.
27 |
--------------------------------------------------------------------------------
/dojo-example/css/app.css:
--------------------------------------------------------------------------------
1 | #clear-completed, #footer, #main,
2 | .plural {
3 | display: none;
4 | }
5 |
6 | #todoapp.todos_selected #clear-completed,
7 | #todoapp.todos_present #footer,
8 | #todoapp.todos_present #main,
9 | .multiple .plural {
10 | display: inherit;
11 | }
12 |
13 | #todo-list li.hidden {
14 | display: none;
15 | }
16 |
17 | #todo-list li .toggle.dijitChecked:after {
18 | color: #85ada7;
19 | text-shadow: 0 1px 0 #669991;
20 | bottom: 1px;
21 | position: relative;
22 | }
23 |
24 | /* When checkbox is selected, score through todo item content */
25 | .dijitCheckBoxChecked + .todo-content {
26 | color: #666;
27 | text-decoration: line-through;
28 | }
29 |
30 | /* When checkbox is selected, score through todo item content after an edit */
31 | .dijitCheckBoxChecked + .dijitInline + .todo-content {
32 | color: #666;
33 | text-decoration: line-through;
34 | }
35 |
36 | #todo-list .dijitCheckBoxInput {
37 | opacity: 0;
38 | position: absolute;
39 | top: 14px;
40 | z-index: 10;
41 | }
42 |
43 | /** Match up inline edit box with styling */
44 | .dijitInputInner {
45 | position: relative;
46 | margin: 0;
47 | width: 100%;
48 | font-size: 24px;
49 | font-family: inherit;
50 | line-height: 1.4em;
51 | border: 0;
52 | outline: none;
53 | color: inherit;
54 | padding: 6px;
55 | border: 1px solid #999;
56 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
57 | -webkit-box-sizing: border-box;
58 | -moz-box-sizing: border-box;
59 | -ms-box-sizing: border-box;
60 | -o-box-sizing: border-box;
61 | box-sizing: border-box;
62 | -webkit-font-smoothing: antialiased;
63 | -moz-font-smoothing: antialiased;
64 | -ms-font-smoothing: antialiased;
65 | -o-font-smoothing: antialiased;
66 | font-smoothing: antialiased;
67 | }
68 |
69 | #todo-list li .dijitInputInner {
70 | display: block;
71 | width: 506px;
72 | padding: 13px 17px 12px 17px;
73 | margin: 0 0 0 43px;
74 | z-index: 10;
75 | }
76 |
77 | /** Ugh, force override of edit container margin */
78 | #todo-list li span.dijitInline {
79 | margin: 0 !important;
80 | }
81 |
82 | /**
83 | * Inline edit box doesn't provide indication via class names
84 | * when a box is 'live'. Style values are set manually. Use the
85 | * opacity change as indicator... :( */
86 | .inline_edit[style~='0;'] ~ .toggle {
87 | visibility: hidden !important;
88 | }
89 |
90 | .dijitOffScreen { /* For 1.8 in-line edit box */
91 | position: absolute !important;
92 | left: 50% !important;
93 | top: -10000px !important;
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/dojo-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dojo • TodoMVC
7 |
8 |
9 |
10 |
11 |
12 |
17 |
19 |
20 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/dojo-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "loader": {
3 | "script": "dojo",
4 | "options": {
5 | "packages": [
6 | { "name": "todo", "location": "js/todo" },
7 | { "name": "dojo", "location": "node_modules/dojo" },
8 | { "name": "dojox", "location": "node_modules/dojox" },
9 | { "name": "dijit", "location": "node_modules/dijit" }
10 | ]
11 | }
12 | },
13 | "functionalSuites": "tests/functional/Todo",
14 | "filterErrorStack": false,
15 | "suites": "tests/all",
16 | "environments": [ "chrome", "node" ],
17 | "configs": {
18 | "ie": {
19 | "environments": {
20 | "browserName": "internet explorer",
21 | "requireWindowFocus": true
22 | },
23 | "tunnelOptions": {
24 | "version": "3.4.0",
25 | "drivers": [{ "name": "ie", "version": "3.4.0" }]
26 | }
27 | },
28 | "firefox": {
29 | "environments": "firefox",
30 | "tunnelOptions": {
31 | "drivers": [{ "name": "firefox" }]
32 | }
33 | },
34 | "edge": {
35 | "environments": "MicrosoftEdge",
36 | "tunnelOptions": {
37 | "drivers": [{ "name": "edge" }]
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/CssToggleWidget.js:
--------------------------------------------------------------------------------
1 | define([
2 | "dojo/_base/array",
3 | "dojo/_base/declare",
4 | "dojo/_base/lang",
5 | "dojo/dom-class",
6 | "dijit/_WidgetBase"
7 | ], function(array, declare, lang, domClass, _WidgetBase){
8 | return declare(_WidgetBase, {
9 | // summary:
10 | // Widget supporting widget attributes with classExists type.
11 | // classExists type allows boolean value of an attribute to reflect existence of a CSS class in a DOM node in the widget.
12 | // example:
13 | // In this example, the text will be bold when the check box is checked.
14 | // |
15 | // |
16 | // |
20 | // |
25 | // |
26 | // |
27 | // |
28 | // |
29 | // | This text will be bold when above check box is checked.
32 | // |
33 | // |
34 |
35 | _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
36 | // summary:
37 | // Handle widget attribute with classExists type.
38 | // See dijit/_WidgetBase._attrToDom() for more details.
39 |
40 | var callee = arguments.callee;
41 | array.forEach((function(){ return lang.isArray(commands) ? commands.slice(0) : [commands]; })(arguments.length >= 3 ? commands : this.attributeMap[attr]), function(command){
42 | command.type != "classExists" ?
43 | this.inherited("_attrToDom", lang.mixin([attr, value, command], {callee: callee})) :
44 | domClass.toggle(this[command.node || "domNode"], command.className || attr, value);
45 | }, this);
46 | }
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/TodoList.js:
--------------------------------------------------------------------------------
1 | define([
2 | "dojo/_base/declare",
3 | "dijit/_TemplatedMixin",
4 | "dijit/_WidgetsInTemplateMixin",
5 | "dojox/mvc/WidgetList",
6 | "dojox/mvc/_InlineTemplateMixin",
7 | "todo/CssToggleWidget",
8 | "todo/ctrl/_HashCompletedMixin"
9 | ], function(declare, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetList, _InlineTemplateMixin, CssToggleWidget, _HashCompletedMixin){
10 | return declare([WidgetList, _InlineTemplateMixin], {
11 | childClz: declare([CssToggleWidget, _TemplatedMixin, _WidgetsInTemplateMixin, _HashCompletedMixin], {
12 | _setCompletedAttr: {type: "classExists", className: "completed"},
13 | _setHiddenAttr: {type: "classExists", className: "hidden"},
14 |
15 | onRemoveClick: function(){
16 | this.parent.listCtrl.removeItem(this.uniqueId);
17 | },
18 |
19 | onEditBoxChange: function(){
20 | if(!this.editBox.value){
21 | this.parent.listCtrl.removeItem(this.uniqueId);
22 | }
23 | }
24 | })
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/app.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
24 |
25 |
47 |
48 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/app18.js:
--------------------------------------------------------------------------------
1 | define([
2 | "dojo/_base/declare",
3 | "dojo/_base/lang",
4 | "dojo/_base/unload",
5 | "dojo/keys",
6 | "dojo/string",
7 | "dijit/_TemplatedMixin",
8 | "dijit/_WidgetsInTemplateMixin",
9 | "todo/CssToggleWidget",
10 | "dojo/text!./app18.html",
11 | // Below modules are referred in template.
12 | // dijit/_WidgetsInTemplateMixin requires all modules referred in template to have been loaded before it's instantiated.
13 | "dijit/form/CheckBox",
14 | "dijit/form/TextBox",
15 | "dojox/mvc/at",
16 | "todo/TodoList",
17 | "todo/ctrl/RouteController",
18 | "todo/ctrl/TodoListRefController",
19 | "todo/ctrl/TodoRefController",
20 | "todo/form/InlineEditBox",
21 | "todo/misc/HashSelectedConverter",
22 | "todo/misc/LessThanOrEqualToConverter",
23 | "todo/model/SimpleTodoModel",
24 | "todo/store/LocalStorage"
25 | ], function(declare, lang, unload, keys, string, _TemplatedMixin, _WidgetsInTemplateMixin, CssToggleWidget, template){
26 | return declare([CssToggleWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
27 | // summary:
28 | // A widgets-in-template widget that composes the application UI of TodoMVC (Dojo 1.8 version).
29 | // Also, this class inherits todo/CssToggleWidget so that it can react to change in "present"/"complete" attributes and add/remove CSS class to the root DOM node of this widget.
30 |
31 | // templateString: String
32 | // The HTML of widget template.
33 | templateString: template,
34 |
35 | // _setPresentAttr: Object
36 | // A object used by todo/CssToggleWidget to reflect true/false state of "present" attribute to existence of "todos_present" CSS class in this widget's root DOM.
37 | _setPresentAttr: {type: "classExists", className: "todos_present"},
38 |
39 | // _setCompleteAttr: Object
40 | // A object used by todo/CssToggleWidget to reflect true/false state of "complete" attribute to existence of "todos_selected" CSS class in this widget's root DOM.
41 | _setCompleteAttr: {type: "classExists", className: "todos_selected"},
42 |
43 | startup: function(){
44 | // summary:
45 | // A function called after the DOM fragment declaring this controller is added to the document.
46 | // See documentation for dijit/_WidgetBase.startup() for more details.
47 |
48 | var _self = this;
49 | this.inherited(arguments);
50 | unload.addOnUnload(function(){
51 | _self.destroy(); // When this page is being unloaded, call destroy callbacks of inner-widgets to let them clean up
52 | });
53 | },
54 |
55 | onKeyPressNewItem: function(/*DOMEvent*/ event){
56 | // summary:
57 | // Handle key press events for the input field for new todo.
58 | // description:
59 | // If user has pressed enter, add current text value as new todo item in the model.
60 |
61 | if(event.keyCode !== keys.ENTER || !string.trim(event.target.value).length){
62 | return; // If the key is not Enter, or the input field is empty, bail
63 | }
64 |
65 | lang.getObject(this.id + "_listCtrl").addItem(event.target.value); // Add a todo item with the given text
66 | event.target.value = ""; // Clear the input field
67 | event.preventDefault();
68 | event.stopPropagation();
69 | }
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/ctrl/RouteController.js:
--------------------------------------------------------------------------------
1 | define([
2 | "dojo/_base/declare",
3 | "dojo/router",
4 | "dijit/Destroyable",
5 | "dojox/mvc/_Controller"
6 | ], function(declare, router, Destroyable, _Controller){
7 | return declare([_Controller, Destroyable], {
8 | // summary:
9 | // A controller that maintains hash attribute in sync with location.hash.
10 | // example:
11 | // In this example, the text box is in sync with the URL hash.
12 | // (The change in URL hash is reflected to the text box, and the edit in text box will be reflected to the URL hash)
13 | // |
14 | // |
15 | // |
19 | // |
20 | // |
21 | // |
22 | // |
23 | // |
25 | // |
26 | // |
27 |
28 | postscript: function(/*Object?*/ params, /*DOMNode?*/ srcNodeRef){
29 | // summary:
30 | // Kicks off instantiation of this controller, in a similar manner as dijit/_WidgetBase.postscript().
31 | // params: Object?
32 | // The optional parameters for this controller.
33 | // srcNodeRef: DOMNode?
34 | // The DOM node declaring this controller. Set if this controller is created via Dojo parser.
35 |
36 | this.inherited(arguments);
37 | srcNodeRef && srcNodeRef.setAttribute("widgetId", this.id); // If this is created via Dojo parser, set widgetId attribute so that destroyDescendants() of parent widget works
38 | },
39 |
40 | startup: function(){
41 | // summary:
42 | // A function called after the DOM fragment declaring this controller is added to the document, in a similar manner as dijit/_WidgetBase.startup().
43 |
44 | var _self = this;
45 | this.own(router.register(/.*/, function(e){ // Register a route handling callback for any route, make sure it's cleaned up upon this controller being destroyed
46 | _self._set("hash", e.newPath); // Update hash property
47 | }));
48 | router.startup(); // Activate dojo/router
49 | this.set("hash", router._currentPath); // Set the inital value of hash property
50 | },
51 |
52 | _setHashAttr: function(value){
53 | // summary:
54 | // Handler for calls to set("hash", val).
55 | // description:
56 | // If the new value is different from location.hash, updates location.hash.
57 |
58 | if(this.hash != value){
59 | router.go(value); // If the new value is different from location.hash, updates location.hash
60 | }
61 | this._set("hash", value); // Assign the new value to the property
62 | }
63 | });
64 | })
65 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/ctrl/TodoListRefController.js:
--------------------------------------------------------------------------------
1 | define([
2 | "dojo/_base/array",
3 | "dojo/_base/declare",
4 | "dojo/Stateful",
5 | "dojox/mvc/ModelRefController"
6 | ], function(array, declare, Stateful, ModelRefController){
7 | return declare(ModelRefController, {
8 | // summary:
9 | // Our custom controller that does:
10 | //
11 | // - Handle actions like adding/removing/marking
12 | // - Provide references to the todo list in data model, whose data comes from above Dojo Object Store
13 | //
14 | // description:
15 | // The todo list in the data model, which is based on dojox/mvc/StatefulArray, can be referred via this[this._refModelProp].
16 | // Actions are implemented in the manner of manipulating array.
17 | // The change will automatically be reflected to the UI via the notification system of dojox/mvc/StatefulArray.
18 |
19 | addItem: function(/*String*/ title){
20 | // summary:
21 | // Adds a todo item with the given title.
22 | // title: String
23 | // The title of todo item.
24 |
25 | this[this._refModelProp].push(new Stateful({title: title, completed: false}));
26 | },
27 |
28 | removeItem: function(/*String*/ uniqueId){
29 | // summary:
30 | // Removes a todo item having the given unique ID.
31 | // uniqueId: String
32 | // The unique ID of the todo item to be removed.
33 |
34 | var model = this[this._refModelProp],
35 | indices = array.filter(array.map(model, function(item, idx){ return item.uniqueId == uniqueId ? idx : -1; }), function(idx){ return idx >= 0; }); // The array index of the todo item to bd removed
36 | if(indices.length > 0){
37 | model.splice(indices[0], 1);
38 | }
39 | },
40 |
41 | removeCompletedItems: function(){
42 | // summary:
43 | // Removes todo items that have been marked as complete.
44 |
45 | var model = this[this._refModelProp];
46 | for(var i = model.length - 1; i >= 0; --i){
47 | if(model[i].get("completed")){
48 | model.splice(i, 1);
49 | }
50 | }
51 | },
52 |
53 | markAll: function(/*Boolean*/ markComplete){
54 | // summary:
55 | // Mark all todo items as complete or incomplete.
56 | // markComplete: Boolean
57 | // True to mark all todo items as complete. Otherwise to mark all todo items as incomplete.
58 |
59 | array.forEach(this[this._refModelProp], function(item){
60 | item.set("completed", markComplete);
61 | });
62 | }
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/ctrl/_HashCompletedMixin.js:
--------------------------------------------------------------------------------
1 | define(["dojo/_base/declare"], function(declare){
2 | var ACTIVE = "/active",
3 | COMPLETED = "/completed";
4 |
5 | function getHiddenState(/*Object*/ props){
6 | // summary:
7 | // Returns the new hidden state of todo item, given the URL hash and the completed state of todo item.
8 | // props: Object
9 | // An object containing the URL hash and the completed state of todo item.
10 |
11 | return props.hash == ACTIVE ? props.completed :
12 | props.hash == COMPLETED ? !props.completed :
13 | false;
14 | }
15 |
16 | return declare(null, {
17 | // summary:
18 | // A mix-in class for widgets-in-template for todo item, that looks at URL hash and completed state of todo item, and updates the hidden state.
19 | // description:
20 | // A todo item should be hidden if:
21 | //
22 | // - URL hash is "/active" and the todo item is complete -OR-
23 | // - URL hash is "/copleted" and the todo item is incomplete
24 |
25 | _setHashAttr: function(/*String*/ value){
26 | // summary:
27 | // Handler for calls to set("hash", val), to update hidden state given the new value and the completed state.
28 |
29 | this.set("hidden", getHiddenState({hash: value, completed: this.completed})); // Update hidden state given the new value and the completed state
30 | this._set("hash", value); // Assign the new value to the attribute
31 | },
32 |
33 | _setCompletedAttr: function(/*Boolean*/ value){
34 | // summary:
35 | // Handler for calls to set("completed", val), to update hidden state given the new value and the hash.
36 |
37 | this.set("hidden", getHiddenState({hash: this.hash, completed: value})); // Update hidden state given the new value and the hash
38 | this._set("completed", value); // Assign the new value to the attribute
39 | }
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/form/CheckBox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * There's an incompatibility between the Dojo CheckBox and the Dojo MVC
3 | * module. To use them together, I've manually tied the "checked" attribute
4 | * value to push updates to the "value" attribute, which the Dojo MVC module
5 | * expects.
6 | */
7 | define(["dojo/_base/declare", "dijit/form/CheckBox"], function (declare, CheckBox) {
8 | return declare("todo.form.CheckBox", [CheckBox], {
9 | _setCheckedAttr: function (checked) {
10 | this.inherited(arguments);
11 | this._watchCallbacks("value", !checked, checked);
12 | },
13 |
14 | _getValueAttr: function () {
15 | return this.get("checked");
16 | }
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/form/InlineEditBox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Extension to the "InlineEditBox" widget to support editing on double click
3 | * events rather than single clicks. We need to override base "postMixInProperties"
4 | * lifecycle method where the event handlers are setup. This method is just a clone
5 | * of that code with the modified event list.
6 | */
7 | define([
8 | "dijit/InlineEditBox",
9 | "dojo/_base/declare",
10 | "dojo/_base/lang",
11 | "dojo/dom-class"
12 | ], function (InlineEditBox, declare, lang, domClass) {
13 |
14 | InlineEditBox._InlineEditor.prototype._onChange = function () {
15 | if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
16 | this._onBlur();
17 | }
18 |
19 | };
20 | return declare("todo.form.InlineEditBox", InlineEditBox, {
21 |
22 | postMixInProperties: function () {
23 | // save pointer to original source node, since Widget nulls-out srcNodeRef
24 | this.displayNode = this.srcNodeRef;
25 |
26 | // connect handlers to the display node
27 | var events = {
28 | ondblclick: "_onClick",
29 | onmouseover: "_onMouseOver",
30 | onmouseout: "_onMouseOut",
31 | onfocus: "_onMouseOver",
32 | onblur: "_onMouseOut"
33 | };
34 | for(var name in events){
35 | this.connect(this.displayNode, name, events[name]);
36 | }
37 | this.displayNode.setAttribute("role", "button");
38 | if(!this.displayNode.getAttribute("tabIndex")){
39 | this.displayNode.setAttribute("tabIndex", 0);
40 | }
41 |
42 | if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
43 | this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
44 | (this.displayNode.innerText||this.displayNode.textContent||""));
45 | }
46 | if(!this.value){
47 | this.displayNode.innerHTML = this.noValueIndicator;
48 | }
49 |
50 | domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
51 | },
52 |
53 | _onChange: function () {
54 | this.inherited(arguments);
55 | this._onBlur();
56 | }
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/misc/HashSelectedConverter.js:
--------------------------------------------------------------------------------
1 | define({
2 | // summary:
3 | // A dojox/mvc data converter, that runs between todo/ctrl/HashController and a widget having tag as its DOM node.
4 | // It does one-way conversion from URL hash to boolean state of whether the URL hash matches href attribute of the widget's DOM node.
5 |
6 | format: function(/*String*/ value){
7 | // summary:
8 | // Returns whether given value matches href attribute of the widget's DOM node.
9 |
10 | return this.target.domNode.getAttribute("href").substr(1) == (value || "/");
11 | },
12 |
13 | parse: function(/*Boolean*/ value){
14 | // summary:
15 | // This functions throws an error so that the new value won't be reflected.
16 |
17 | throw new Error();
18 | }
19 | });
20 |
--------------------------------------------------------------------------------
/dojo-example/js/todo/misc/LessThanOrEqualToConverter.js:
--------------------------------------------------------------------------------
1 | define({
2 | // summary:
3 | // A dojox/mvc data converter, that does one-way conversion that returns whether we have less than n todo items in a specific state, where n is the given number in data converter options.
4 | // Data converter options can be specified by setting constraints property in one of data binding endpoints.
5 | // See data converter section of dojox/mvc/sync library's documentation for more details.
6 |
7 | format: function(/*Number*/ value, /*Object*/ constraints){
8 | // summary:
9 | // Returns whether given value is less than or equal to the given number in data converter options (default zero).
10 |
11 | return value <= (constraints.lessThanOrEqualTo || 0);
12 | },
13 |
14 | parse: function(/*Boolean*/ value){
15 | // summary:
16 | // This functions throws an error so that the new value won't be reflected.
17 |
18 | throw new Error();
19 | }
20 | });
21 |
--------------------------------------------------------------------------------
/dojo-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dojo-intern-example",
3 | "description": "An example of using Dojo and Dijit with Intern",
4 | "version": "0.2.0",
5 | "private": true,
6 | "devDependencies": {
7 | "intern": "~4.1.0"
8 | },
9 | "dependencies": {
10 | "dijit": "~1.12.9",
11 | "dojo": "~1.12.8",
12 | "dojox": "~1.14.6",
13 | "todomvc-common": "~0.1.6"
14 | },
15 | "scripts": {
16 | "test": "intern"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/dojo-example/tests/all.js:
--------------------------------------------------------------------------------
1 | define([
2 | './model/SimpleTodoModel',
3 | 'dojo/has!host-browser?./store/LocalStorage',
4 | 'dojo/has!host-browser?./form/CheckBox'
5 | ], function () {});
6 |
--------------------------------------------------------------------------------
/dojo-example/tests/form/CheckBox.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'require',
3 | 'todo/form/CheckBox'
4 | ], function (require, CheckBox) {
5 | var registerSuite = intern.getInterface('object').registerSuite;
6 | var assert = intern.getPlugin('chai').assert;
7 | var checkbox;
8 |
9 | registerSuite('CheckBox', {
10 | before: function () {
11 | checkbox = new CheckBox();
12 | },
13 |
14 | tests: {
15 | 'get value': function () {
16 | checkbox.set('value', 'arbitraryTitle');
17 | checkbox.set('checked', true);
18 | assert.strictEqual(checkbox.get('value'), true);
19 | },
20 |
21 | 'set checked': function () {
22 | checkbox.set('checked', true);
23 | assert.strictEqual(checkbox.get('checked'), true);
24 | }
25 | }
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/dojo-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | define([ 'dojo/node!@theintern/leadfoot/keys' ], function (keysModule) {
2 | var registerSuite = intern.getInterface('object').registerSuite;
3 | var assert = intern.getPlugin('chai').assert;
4 | var keys = keysModule.default;
5 |
6 | registerSuite('Todo (functional)', {
7 | 'submit form': function () {
8 | return this.remote
9 | .get('index.html')
10 | .setFindTimeout(60000)
11 | .findById('new-todo')
12 | .type('Task 1')
13 | .type(keys.RETURN)
14 | .type('Task 2')
15 | .type(keys.RETURN)
16 | .type('Task 3')
17 | .getSpecAttribute('value')
18 | .then(function (val) {
19 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
20 | });
21 | }
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/dojo-example/tests/model/SimpleTodoModel.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'require',
3 | 'todo/model/SimpleTodoModel'
4 | ], function (require, SimpleTodoModel) {
5 | var registerSuite = intern.getInterface('object').registerSuite;
6 | var assert = intern.getPlugin('chai').assert;
7 |
8 | registerSuite('SimpleTodoModel', {
9 | 'default data': function () {
10 | var emptyModel = new SimpleTodoModel();
11 | assert.strictEqual(emptyModel.get('id'), 'todos-dojo', 'Id should default to "todos-dojo"');
12 | assert.strictEqual(emptyModel.get('todos').length, 0, 'Todos array should default to an empty array.');
13 | assert.strictEqual(emptyModel.get('incomplete'), 0, 'Incomplete count should default to 0.');
14 | assert.strictEqual(emptyModel.get('complete'), 0, 'Incomplete count should default to 0.');
15 | },
16 |
17 | 'get incomplete (empty model)': function () {
18 | var emptyModel = new SimpleTodoModel();
19 | emptyModel.todos.push({});
20 | emptyModel.todos.push({});
21 | assert.strictEqual(emptyModel.get('incomplete'), 2, 'Prepopulated model todos should determine incomplete model property.');
22 | emptyModel.set('complete', 2);
23 | assert.strictEqual(emptyModel.get('incomplete'), 0, 'Incomplete count should change when complete count is manually updated.');
24 | },
25 |
26 | 'get complete (empty model)': function () {
27 | var emptyModel = new SimpleTodoModel();
28 | emptyModel.todos.push({});
29 | emptyModel.todos.push({});
30 | assert.strictEqual(emptyModel.get('complete'), 0, 'Prepopulated model todos should determine complete model property.');
31 | emptyModel.set('incomplete', 0);
32 | assert.strictEqual(emptyModel.get('complete'), 2, 'Complete count should change when incomplete count is manually updated.');
33 | }
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/dojo-example/tests/store/LocalStorage.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'require',
3 | 'todo/store/LocalStorage'
4 | ], function (require, LocalStorage) {
5 | var registerSuite = intern.getInterface('object').registerSuite;
6 | var assert = intern.getPlugin('chai').assert;
7 | var store;
8 |
9 | registerSuite('LocalStorage Store', {
10 | before: function () {
11 | store = new LocalStorage({
12 | data: [
13 | {id: 1, name: 'one', prime: false},
14 | {id: 2, name: 'two', even: true, prime: true},
15 | {id: 3, name: 'three', prime: true},
16 | {id: 4, name: 'four', even: true, prime: false},
17 | {id: 5, name: 'five', prime: true}
18 | ]
19 | });
20 | },
21 |
22 | tests: {
23 | get: function () {
24 | assert.strictEqual(store.get(4).name, 'four', 'Store should get correct item based on id.');
25 | assert.isTrue(store.get(5).prime, 'Store should get correct item based on id.');
26 | },
27 |
28 | getIdentity: function () {
29 | var item = store.get(3);
30 | assert.strictEqual(store.getIdentity(item), 3, 'Identifying property (id) should be returned for item.');
31 | },
32 |
33 | put: function () {
34 | var four = store.get(4);
35 | four.square = true;
36 | store.put(four);
37 | four = store.get(4);
38 | assert.isTrue(four.square, 'Item should be updated after modification.');
39 | },
40 |
41 | add: function () {
42 | store.put({
43 | id: 6,
44 | perfect: true
45 | });
46 | assert.isTrue(store.get(6).perfect, 'New item should be added to the store.');
47 | },
48 |
49 | query: function () {
50 | var results = store.query({prime: true});
51 | assert.strictEqual(results.length, 3, 'Three prime results should be found.');
52 | }
53 | }
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/electron-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"],
3 | "plugins": [
4 | "transform-object-rest-spread",
5 | "transform-react-jsx",
6 | "transform-class-properties"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/electron-example/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/electron-example/README.md:
--------------------------------------------------------------------------------
1 | # electron-example
2 |
3 | This example is based on the [React Redux example](https://github.com/reactjs/redux/tree/master/examples/todomvc),
4 | running in Electron. It contains two sets of functional tests, one that uses Intern’s built-in WebDriver library, and
5 | one that uses Spectron. It also contains unit tests, but these are run in Node rather than Electron as Intern 4.x does
6 | not currently support running unit tests in Electron.
7 |
8 | ## Setup
9 |
10 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
11 |
12 | 2. Install node modules
13 |
14 | ```
15 | $ npm install
16 | ```
17 |
18 | 3. Build the example
19 | ```
20 | $ npm run build
21 | ```
22 |
23 | ## Running Tests
24 |
25 | **Unit tests (in Node) and functional tests**
26 |
27 | $ npm test
28 |
29 | On Windows, run
30 |
31 | $ npm test config=@windows
32 |
33 | **WebDriver tests using Spectron**
34 |
35 | $ npm test config=@spectron
36 |
--------------------------------------------------------------------------------
/electron-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "functionalSuites": "tests/functional/app.js",
3 | "node": {
4 | "suites": "tests/unit/**/*.js",
5 | "plugins": ["tests/build_check.js", "tests/pre.js"]
6 | },
7 | "environments": [
8 | "node",
9 | {
10 | "browserName": "chrome",
11 | "fixSessionCapabilities": false,
12 | "chromeOptions": {
13 | "binary": "{pwd}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron",
14 | "args": ["app={pwd}/build/bootstrap.js"]
15 | }
16 | }
17 | ],
18 | "tunnelOptions": {
19 | "version": "3.4.0",
20 | "drivers": [
21 | {
22 | "name": "chrome",
23 | "version": "2.24"
24 | }
25 | ]
26 | },
27 | "filterErrorStack": true,
28 | "configs": {
29 | "spectron": {
30 | "suites": "tests/functional/spectron.js",
31 | "environments": [],
32 | "node": {
33 | "suites": []
34 | }
35 | },
36 |
37 | "windows": {
38 | "environments": [
39 | "node",
40 | {
41 | "browserName": "chrome",
42 | "fixSessionCapabilities": false,
43 | "chromeOptions": {
44 | "binary": "{pwd}/node_modules/electron/dist/electron.exe",
45 | "args": ["app={pwd}/build/bootstrap.js"]
46 | }
47 | }
48 | ]
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/electron-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "electron-example",
4 | "version": "0.1.0",
5 | "description": "An example showing how to use Intern to test an Electron app",
6 | "homepage": "./",
7 | "dependencies": {
8 | "babel-core": "~6.23.0",
9 | "babel-plugin-transform-class-properties": "~6.23.0",
10 | "babel-plugin-transform-object-rest-spread": "~6.23.0",
11 | "babel-plugin-transform-react-jsx": "~6.23.0",
12 | "babel-preset-env": "~1.3.3",
13 | "classnames": "~2.2.5",
14 | "electron": "~1.6.11",
15 | "react": "~15.5.0",
16 | "react-dom": "~15.5.0",
17 | "react-redux": "~5.0.0",
18 | "redux": "~3.6.0",
19 | "todomvc-app-css": "~2.0.6"
20 | },
21 | "devDependencies": {
22 | "enzyme": "~2.8.0",
23 | "intern": "~4.3.0",
24 | "react-addons-test-utils": "~15.5.0",
25 | "react-scripts": "~0.9.3",
26 | "react-test-renderer": "~15.5.0",
27 | "serve": "~5.1.1",
28 | "shx": "~0.3.2",
29 | "spectron": "~3.7.2"
30 | },
31 | "scripts": {
32 | "build": "react-scripts build && shx cp src/main.js build && shx cp src/bootstrap.js build",
33 | "start": "cd build && electron bootstrap.js",
34 | "clean": "shx rm -rf build",
35 | "test": "intern"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/electron-example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redux TodoMVC Example
7 |
8 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/electron-example/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes'
2 |
3 | export const addTodo = text => ({ type: types.ADD_TODO, text })
4 | export const deleteTodo = id => ({ type: types.DELETE_TODO, id })
5 | export const editTodo = (id, text) => ({ type: types.EDIT_TODO, id, text })
6 | export const completeTodo = id => ({ type: types.COMPLETE_TODO, id })
7 | export const completeAll = () => ({ type: types.COMPLETE_ALL })
8 | export const clearCompleted = () => ({ type: types.CLEAR_COMPLETED })
9 |
--------------------------------------------------------------------------------
/electron-example/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | process.chdir(__dirname);
3 | require('./main.js');
4 |
--------------------------------------------------------------------------------
/electron-example/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react'
2 | import classnames from 'classnames'
3 | import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'
4 |
5 | const FILTER_TITLES = {
6 | [SHOW_ALL]: 'All',
7 | [SHOW_ACTIVE]: 'Active',
8 | [SHOW_COMPLETED]: 'Completed'
9 | }
10 |
11 | export default class Footer extends Component {
12 | static propTypes = {
13 | completedCount: PropTypes.number.isRequired,
14 | activeCount: PropTypes.number.isRequired,
15 | filter: PropTypes.string.isRequired,
16 | onClearCompleted: PropTypes.func.isRequired,
17 | onShow: PropTypes.func.isRequired
18 | }
19 |
20 | renderTodoCount() {
21 | const { activeCount } = this.props
22 | const itemWord = activeCount === 1 ? 'item' : 'items'
23 |
24 | return (
25 |
26 | {activeCount || 'No'} {itemWord} left
27 |
28 | )
29 | }
30 |
31 | renderFilterLink(filter) {
32 | const title = FILTER_TITLES[filter]
33 | const { filter: selectedFilter, onShow } = this.props
34 |
35 | return (
36 | onShow(filter)}>
39 | {title}
40 |
41 | )
42 | }
43 |
44 | renderClearButton() {
45 | const { completedCount, onClearCompleted } = this.props
46 | if (completedCount > 0) {
47 | return (
48 |
52 | )
53 | }
54 | }
55 |
56 | render() {
57 | return (
58 |
69 | )
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/electron-example/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react'
2 | import TodoTextInput from './TodoTextInput'
3 |
4 | export default class Header extends Component {
5 | static propTypes = {
6 | addTodo: PropTypes.func.isRequired
7 | }
8 |
9 | handleSave = text => {
10 | if (text.length !== 0) {
11 | this.props.addTodo(text)
12 | }
13 | }
14 |
15 | render() {
16 | return (
17 |
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/electron-example/src/components/MainSection.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import TodoItem from './TodoItem'
3 | import Footer from './Footer'
4 | import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'
5 |
6 | const TODO_FILTERS = {
7 | [SHOW_ALL]: () => true,
8 | [SHOW_ACTIVE]: todo => !todo.completed,
9 | [SHOW_COMPLETED]: todo => todo.completed
10 | }
11 |
12 | export default class MainSection extends Component {
13 | static propTypes = {
14 | todos: PropTypes.array.isRequired,
15 | actions: PropTypes.object.isRequired
16 | }
17 |
18 | state = { filter: SHOW_ALL }
19 |
20 | handleClearCompleted = () => {
21 | this.props.actions.clearCompleted()
22 | }
23 |
24 | handleShow = filter => {
25 | this.setState({ filter })
26 | }
27 |
28 | renderToggleAll(completedCount) {
29 | const { todos, actions } = this.props
30 | if (todos.length > 0) {
31 | return (
32 |
36 | )
37 | }
38 | }
39 |
40 | renderFooter(completedCount) {
41 | const { todos } = this.props
42 | const { filter } = this.state
43 | const activeCount = todos.length - completedCount
44 |
45 | if (todos.length) {
46 | return (
47 |
52 | )
53 | }
54 | }
55 |
56 | render() {
57 | const { todos, actions } = this.props
58 | const { filter } = this.state
59 |
60 | const filteredTodos = todos.filter(TODO_FILTERS[filter])
61 | const completedCount = todos.reduce((count, todo) =>
62 | todo.completed ? count + 1 : count,
63 | 0
64 | )
65 |
66 | return (
67 |
68 | {this.renderToggleAll(completedCount)}
69 |
70 | {filteredTodos.map(todo =>
71 |
72 | )}
73 |
74 | {this.renderFooter(completedCount)}
75 |
76 | )
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/electron-example/src/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import classnames from 'classnames'
3 | import TodoTextInput from './TodoTextInput'
4 |
5 | export default class TodoItem extends Component {
6 | static propTypes = {
7 | todo: PropTypes.object.isRequired,
8 | editTodo: PropTypes.func.isRequired,
9 | deleteTodo: PropTypes.func.isRequired,
10 | completeTodo: PropTypes.func.isRequired
11 | }
12 |
13 | state = {
14 | editing: false
15 | }
16 |
17 | handleDoubleClick = () => {
18 | this.setState({ editing: true })
19 | }
20 |
21 | handleSave = (id, text) => {
22 | if (text.length === 0) {
23 | this.props.deleteTodo(id)
24 | } else {
25 | this.props.editTodo(id, text)
26 | }
27 | this.setState({ editing: false })
28 | }
29 |
30 | render() {
31 | const { todo, completeTodo, deleteTodo } = this.props
32 |
33 | let element
34 | if (this.state.editing) {
35 | element = (
36 | this.handleSave(todo.id, text)} />
39 | )
40 | } else {
41 | element = (
42 |
43 | completeTodo(todo.id)} />
47 |
50 |
53 | )
54 | }
55 |
56 | return (
57 | -
61 | {element}
62 |
63 | )
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/electron-example/src/components/TodoTextInput.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import classnames from 'classnames'
3 |
4 | export default class TodoTextInput extends Component {
5 | static propTypes = {
6 | onSave: PropTypes.func.isRequired,
7 | text: PropTypes.string,
8 | placeholder: PropTypes.string,
9 | editing: PropTypes.bool,
10 | newTodo: PropTypes.bool
11 | }
12 |
13 | state = {
14 | text: this.props.text || ''
15 | }
16 |
17 | handleSubmit = e => {
18 | const text = e.target.value.trim()
19 | if (e.which === 13) {
20 | this.props.onSave(text)
21 | if (this.props.newTodo) {
22 | this.setState({ text: '' })
23 | }
24 | }
25 | }
26 |
27 | handleChange = e => {
28 | this.setState({ text: e.target.value })
29 | }
30 |
31 | handleBlur = e => {
32 | if (!this.props.newTodo) {
33 | this.props.onSave(e.target.value)
34 | }
35 | }
36 |
37 | render() {
38 | return (
39 |
51 | )
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/electron-example/src/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_TODO = 'ADD_TODO'
2 | export const DELETE_TODO = 'DELETE_TODO'
3 | export const EDIT_TODO = 'EDIT_TODO'
4 | export const COMPLETE_TODO = 'COMPLETE_TODO'
5 | export const COMPLETE_ALL = 'COMPLETE_ALL'
6 | export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
7 |
--------------------------------------------------------------------------------
/electron-example/src/constants/TodoFilters.js:
--------------------------------------------------------------------------------
1 | export const SHOW_ALL = 'show_all'
2 | export const SHOW_COMPLETED = 'show_completed'
3 | export const SHOW_ACTIVE = 'show_active'
4 |
--------------------------------------------------------------------------------
/electron-example/src/containers/App.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import { bindActionCreators } from 'redux'
3 | import { connect } from 'react-redux'
4 | import Header from '../components/Header'
5 | import MainSection from '../components/MainSection'
6 | import * as TodoActions from '../actions'
7 |
8 | const App = ({todos, actions}) => (
9 |
10 |
11 |
12 |
13 | )
14 |
15 | App.propTypes = {
16 | todos: PropTypes.array.isRequired,
17 | actions: PropTypes.object.isRequired
18 | }
19 |
20 | const mapStateToProps = state => ({
21 | todos: state.todos
22 | })
23 |
24 | const mapDispatchToProps = dispatch => ({
25 | actions: bindActionCreators(TodoActions, dispatch)
26 | })
27 |
28 | export default connect(
29 | mapStateToProps,
30 | mapDispatchToProps
31 | )(App)
32 |
--------------------------------------------------------------------------------
/electron-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { createStore } from 'redux'
4 | import { Provider } from 'react-redux'
5 | import App from './containers/App'
6 | import reducer from './reducers'
7 | import 'todomvc-app-css/index.css'
8 |
9 | const store = createStore(reducer)
10 |
11 | render(
12 |
13 |
14 | ,
15 | document.getElementById('root')
16 | )
17 |
--------------------------------------------------------------------------------
/electron-example/src/main.js:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow, protocol } from 'electron';
2 | import { join, resolve } from 'path';
3 | import { URL, format } from 'url';
4 |
5 | // Keep a global reference of the window object, if you don't, the window will
6 | // be closed automatically when the JavaScript object is garbage collected.
7 | let mainWindow;
8 |
9 | function createWindow () {
10 | // Create the browser window.
11 | mainWindow = new BrowserWindow({width: 800, height: 600});
12 |
13 | // and load the index.html of the app.
14 | mainWindow.loadURL(format({
15 | pathname: join(__dirname, 'index.html'),
16 | protocol: 'file:',
17 | slashes: true,
18 | baseURLForDataURL: format({pathname: __dirname + '/', protocol: 'file:'})
19 | }));
20 |
21 | // Emitted when the window is closed.
22 | mainWindow.on('closed', function () {
23 | // Dereference the window object, usually you would store windows
24 | // in an array if your app supports multi windows, this is the time
25 | // when you should delete the corresponding element.
26 | mainWindow = null;
27 | });
28 |
29 | protocol.interceptFileProtocol('file', (request, callback) => {
30 | if (/index.html$/.test(request.url)) {
31 | callback(resolve('./index.html'));
32 | }
33 | else {
34 | let url = new URL(request.url);
35 | callback(resolve(join('.', url.pathname)));
36 | }
37 | });
38 | }
39 |
40 | // This method will be called when Electron has finished
41 | // initialization and is ready to create browser windows.
42 | // Some APIs can only be used after this event occurs.
43 | app.on('ready', createWindow);
44 |
45 | // Quit when all windows are closed.
46 | app.on('window-all-closed', function () {
47 | // On OS X it is common for applications and their menu bar
48 | // to stay active until the user quits explicitly with Cmd + Q
49 | if (process.platform !== 'darwin') {
50 | app.quit();
51 | }
52 | });
53 |
54 | app.on('activate', function () {
55 | // On OS X it's common to re-create a window in the app when the
56 | // dock icon is clicked and there are no other windows open.
57 | if (mainWindow === null) {
58 | createWindow();
59 | }
60 | });
61 |
62 | // In this file you can include the rest of your app's specific main process
63 | // code. You can also put them in separate files and require them here.
64 |
--------------------------------------------------------------------------------
/electron-example/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import todos from './todos'
3 |
4 | const rootReducer = combineReducers({
5 | todos
6 | })
7 |
8 | export default rootReducer
9 |
--------------------------------------------------------------------------------
/electron-example/src/reducers/todos.js:
--------------------------------------------------------------------------------
1 | import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes'
2 |
3 | const initialState = [
4 | {
5 | text: 'Use Redux',
6 | completed: false,
7 | id: 0
8 | }
9 | ]
10 |
11 | export default function todos(state = initialState, action) {
12 | switch (action.type) {
13 | case ADD_TODO:
14 | return [
15 | {
16 | id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
17 | completed: false,
18 | text: action.text
19 | },
20 | ...state
21 | ]
22 |
23 | case DELETE_TODO:
24 | return state.filter(todo =>
25 | todo.id !== action.id
26 | )
27 |
28 | case EDIT_TODO:
29 | return state.map(todo =>
30 | todo.id === action.id ?
31 | { ...todo, text: action.text } :
32 | todo
33 | )
34 |
35 | case COMPLETE_TODO:
36 | return state.map(todo =>
37 | todo.id === action.id ?
38 | { ...todo, completed: !todo.completed } :
39 | todo
40 | )
41 |
42 | case COMPLETE_ALL:
43 | const areAllMarked = state.every(todo => todo.completed)
44 | return state.map(todo => ({
45 | ...todo,
46 | completed: !areAllMarked
47 | }))
48 |
49 | case CLEAR_COMPLETED:
50 | return state.filter(todo => todo.completed === false)
51 |
52 | default:
53 | return state
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/electron-example/src/renderer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is required by the index.html file and will be executed in the
3 | * renderer process for that window. All of the Node.js APIs are available in
4 | * this process.
5 | */
6 |
--------------------------------------------------------------------------------
/electron-example/tests/build_check.js:
--------------------------------------------------------------------------------
1 | intern.on('beforeRun', () => {
2 | if (!require('fs').existsSync('build')) {
3 | throw new Error('Project must be built');
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/electron-example/tests/functional/app.js:
--------------------------------------------------------------------------------
1 | var { assert } = intern.getPlugin('chai');
2 | const { registerSuite } = intern.getInterface('object');
3 |
4 | registerSuite('Application', {
5 | afterEach() {
6 | return this.remote
7 | .refresh()
8 | .sleep(1000);
9 | },
10 |
11 | tests: {
12 | initialized() {
13 | return this.remote
14 | .getPageTitle()
15 | .then(title => assert.equal(title, 'Redux TodoMVC Example'));
16 | },
17 |
18 | 'add item'() {
19 | return this.remote
20 | .setFindTimeout(5000)
21 | .findByCssSelector('input.new-todo')
22 | .type('Task 1\n')
23 | .type('Task 2\n')
24 | .type('Task 3')
25 | .getSpecAttribute('value')
26 | .then(val => assert.include(val, 'Task 3'));
27 | }
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/electron-example/tests/functional/spectron.js:
--------------------------------------------------------------------------------
1 | // A simple test to verify a visible window is opened with a title
2 | import { Application } from 'spectron';
3 | import electronPath from 'electron';
4 |
5 | const { assert } = intern.getPlugin('chai');
6 | const { registerSuite } = intern.getInterface('object');
7 |
8 | let app;
9 |
10 | registerSuite('Application', {
11 | beforeEach() {
12 | app = new Application({
13 | path: electronPath,
14 | args: ['build/bootstrap.js']
15 | });
16 | return app.start();
17 | },
18 |
19 | afterEach() {
20 | return app.stop().then(() => {
21 | app = null;
22 | });
23 | },
24 |
25 | tests: {
26 | initialized() {
27 | return app.browserWindow.isVisible()
28 | .then(isVisible => assert.isTrue(isVisible))
29 | .then(() => app.client.getTitle())
30 | .then(title => assert.equal(title, 'Redux TodoMVC Example'))
31 | },
32 |
33 | 'add item'() {
34 | return app.client
35 | .setValue('input.new-todo', 'Task 1\n')
36 | .setValue('input.new-todo', 'Task 2\n')
37 | .setValue('input.new-todo', 'Task 3')
38 | .getValue('input.new-todo')
39 | .then(val => assert.include(val, 'Task 3'));
40 | }
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/electron-example/tests/pre.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | if (intern.config.showConfig) {
3 | console.log(JSON.stringify(intern.config, null, ' '));
4 | process.exit(0);
5 | }
6 |
--------------------------------------------------------------------------------
/electron-example/tests/unit/actions/index.js:
--------------------------------------------------------------------------------
1 | import * as actions from '../../../src/actions/index';
2 | import * as types from '../../../src/constants/ActionTypes'
3 |
4 | const { describe, it } = intern.getInterface('bdd');
5 | const { expect } = intern.getPlugin('chai');
6 |
7 | describe('todo actions', () => {
8 | it('addTodo should create ADD_TODO action', () => {
9 | expect(actions.addTodo('Use Redux')).to.eql({
10 | type: types.ADD_TODO,
11 | text: 'Use Redux'
12 | })
13 | })
14 |
15 | it('deleteTodo should create DELETE_TODO action', () => {
16 | expect(actions.deleteTodo(1)).to.eql({
17 | type: types.DELETE_TODO,
18 | id: 1
19 | })
20 | })
21 |
22 | it('editTodo should create EDIT_TODO action', () => {
23 | expect(actions.editTodo(1, 'Use Redux everywhere')).to.eql({
24 | type: types.EDIT_TODO,
25 | id: 1,
26 | text: 'Use Redux everywhere'
27 | })
28 | })
29 |
30 | it('completeTodo should create COMPLETE_TODO action', () => {
31 | expect(actions.completeTodo(1)).to.eql({
32 | type: types.COMPLETE_TODO,
33 | id: 1
34 | })
35 | })
36 |
37 | it('completeAll should create COMPLETE_ALL action', () => {
38 | expect(actions.completeAll()).to.eql({
39 | type: types.COMPLETE_ALL
40 | })
41 | })
42 |
43 | it('clearCompleted should create CLEAR_COMPLETED action', () => {
44 | expect(actions.clearCompleted()).to.eql({
45 | type: types.CLEAR_COMPLETED
46 | })
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/grunt-example/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true */
2 | module.exports = function (grunt) {
3 | grunt.initConfig({
4 | intern: {
5 | options: {
6 | excludeInstrumentation: true,
7 | require: 'app/Block.js',
8 | suites: [ 'tests/lib/add.js', 'tests/lib/get.js' ],
9 | reporters: [ 'runner' ]
10 | },
11 | node: {
12 | options: {}
13 | },
14 | browser: {
15 | options: {
16 | environments: 'chrome'
17 | }
18 | }
19 | }
20 | });
21 |
22 | // Loading using a local git copy
23 | grunt.loadNpmTasks('intern');
24 |
25 | // Register a test task
26 | grunt.registerTask('test', ['intern:node']);
27 |
28 | // Register a task for webdriver tests
29 | grunt.registerTask('test:browser', ['intern:browser']);
30 |
31 | // By default we just test
32 | grunt.registerTask('default', ['test']);
33 | };
34 |
--------------------------------------------------------------------------------
/grunt-example/README.md:
--------------------------------------------------------------------------------
1 | grunt-example
2 | =============
3 |
4 | ## Setup
5 |
6 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
7 |
8 | 2. Install grunt-cli
9 | ```
10 | $ npm install -g grunt-cli
11 | ```
12 |
13 | 3. Install node modules
14 | ```
15 | $ npm install
16 | ```
17 |
18 | ## Running Tests
19 |
20 | **Unit tests in Node**
21 |
22 | $ grunt test
23 |
24 | **WebDriver tests**
25 |
26 | $ grunt test:browser
27 |
28 | ## More information
29 |
30 | Check the [Gruntfile](Gruntfile.js) for details. If you haven't used Grunt before, here are some commands that are also
31 | available using the Gruntfile in this example:
32 |
33 | * `grunt` runs the default task in the Gruntfile, which is `test` in this case.
34 | * `grunt intern:node` will run Intern’s Node executor
35 | * `grunt intern:webdriver` will run Intern’s WebDriver executor, which will run unit tests in a browser
36 |
37 | If you need more information check out the [task
38 | documentation](https://github.com/jason0x43/intern/blob/4.0.0-pre/docs/running.md#grunt).
39 |
--------------------------------------------------------------------------------
/grunt-example/app/Block.js:
--------------------------------------------------------------------------------
1 | (function (global) {
2 | var Block = function (items) {
3 | items = items || [];
4 | this.contrainer = items;
5 | };
6 |
7 | Block.prototype.has = function (item) {
8 | return this.contrainer.indexOf(item) != -1;
9 | };
10 |
11 | Block.prototype.add = function (item) {
12 | return this.contrainer.push(item);
13 | };
14 |
15 | Block.prototype.getSize = function () {
16 | return this.contrainer.length;
17 | };
18 |
19 | global.Block = Block;
20 | })(typeof window === 'undefined' ? global : window);
21 |
--------------------------------------------------------------------------------
/grunt-example/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | grunt-intern-example
6 |
7 |
8 | Block App
9 |
10 |
11 |
12 |
13 |
14 |
31 |
32 |
--------------------------------------------------------------------------------
/grunt-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "intern-grunt-example",
3 | "description": "An example of an app that uses grunt-intern",
4 | "version": "0.2.0",
5 | "private": true,
6 | "devDependencies": {
7 | "grunt": "~1.0.1",
8 | "intern": "~4.0.0-alpha.10"
9 | },
10 | "scripts": {
11 | "test": "grunt test"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/grunt-example/tests/lib/add.js:
--------------------------------------------------------------------------------
1 | /* global intern, Block */
2 |
3 | var tdd = intern.getInterface('tdd');
4 | var assert = intern.getPlugin('chai').assert;
5 |
6 | var suite = tdd.suite;
7 | var test = tdd.test;
8 |
9 | suite('add', function () {
10 | test('#addItems', function () {
11 | var aSet = new Block();
12 | aSet.add(3);
13 | aSet.add('text');
14 | assert.equal(aSet.has(3), true);
15 | assert.equal(aSet.has('text'), true);
16 | assert.equal(aSet.has(5), false);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/grunt-example/tests/lib/get.js:
--------------------------------------------------------------------------------
1 | /* global intern, Block */
2 |
3 | var tdd = intern.getInterface('tdd');
4 | var assert = intern.getPlugin('chai').assert;
5 |
6 | var suite = tdd.suite;
7 | var test = tdd.test;
8 |
9 | suite('get', function () {
10 | test('#getSize', function () {
11 | var bSet = new Block();
12 | assert.equal(bSet.getSize(), 0);
13 | });
14 |
15 | test('#getSizewithItems', function () {
16 | var eSet = new Block([1, 2, 3]);
17 | assert.equal(eSet.getSize(), 3);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/jquery-example/README.md:
--------------------------------------------------------------------------------
1 | jquery-example
2 | =============
3 |
4 | This example is based on the [TodoMVC jQuery Example](http://todomvc.com/examples/jquery/).
5 |
6 | ## Setup
7 |
8 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
9 |
10 | 2. Install node modules
11 | ```
12 | npm install
13 | ```
14 |
15 | ## Running tests
16 |
17 | To run the unit and functional tests in Chrome:
18 |
19 | ```
20 | $ npm test
21 | ```
22 |
--------------------------------------------------------------------------------
/jquery-example/css/app.css:
--------------------------------------------------------------------------------
1 | #main,
2 | #footer {
3 | display: none;
4 | }
5 |
--------------------------------------------------------------------------------
/jquery-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | jQuery • TodoMVC
7 |
8 |
9 |
10 |
11 |
26 |
31 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/jquery-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "environments": "chrome",
3 | "functionalSuites": "tests/functional/**/*.js",
4 | "browser": {
5 | "plugins": "js/utils.js",
6 | "suites": "tests/utils.js"
7 | },
8 | "configs": {
9 | "firefox": {
10 | "environments": "firefox",
11 | "tunnelOptions": {
12 | "drivers": ["firefox"]
13 | }
14 | },
15 | "ie": {
16 | "environments": "internet explorer",
17 | "tunnelOptions": {
18 | "drivers": ["ie"]
19 | }
20 | },
21 | "edge": {
22 | "environments": "MicrosoftEdge",
23 | "tunnelOptions": {
24 | "drivers": ["edge"]
25 | }
26 | },
27 | "safari": {
28 | "environments": {
29 | "browser": "safari",
30 | "fixSessionCapabilities": "no-detect"
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/jquery-example/js/utils.js:
--------------------------------------------------------------------------------
1 | window.Utils = {
2 | uuid: function () {
3 | /*jshint bitwise:false */
4 | var i, random;
5 | var uuid = '';
6 |
7 | for (i = 0; i < 32; i++) {
8 | random = Math.random() * 16 | 0;
9 | if (i === 8 || i === 12 || i === 16 || i === 20) {
10 | uuid += '-';
11 | }
12 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16);
13 | }
14 |
15 | return uuid;
16 | },
17 | pluralize: function (count, word) {
18 | return count === 1 ? word : word + 's';
19 | },
20 | store: function (namespace, data) {
21 | if (arguments.length > 1) {
22 | return localStorage.setItem(namespace, JSON.stringify(data));
23 | } else {
24 | var store = localStorage.getItem(namespace);
25 | return (store && JSON.parse(store)) || [];
26 | }
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/jquery-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "jquery-intern-example",
4 | "version": "0.2.0",
5 | "description": "An example showing a typical jQuery application tested with Intern",
6 | "devDependencies": {},
7 | "dependencies": {
8 | "handlebars": "~4.3.0",
9 | "intern": "~4.1.2",
10 | "jquery": "~3.5.0",
11 | "todomvc-common": "~0.1.4"
12 | },
13 | "scripts": {
14 | "test": "intern"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/jquery-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | /* global intern */
2 | var registerSuite = intern.getInterface('object').registerSuite;
3 | var assert = intern.getPlugin('chai').assert;
4 |
5 | registerSuite('Todo (functional)', {
6 | 'submit form': function () {
7 | return this.remote
8 | .get('./index.html')
9 | .findById('new-todo')
10 | .click()
11 | .pressKeys('Task 1')
12 | .pressKeys('\n')
13 | .pressKeys('Task 2')
14 | .pressKeys('\n')
15 | .pressKeys('Task 3')
16 | .getSpecAttribute('value')
17 | .then(function (val) {
18 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
19 | });
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/jquery-example/tests/utils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | /* global intern */
3 | var registerSuite = intern.getInterface('object').registerSuite;
4 | var assert = intern.getPlugin('chai').assert;
5 |
6 | registerSuite('utils', {
7 | uuid: function () {
8 | var uuid = window.Utils.uuid(),
9 | validUUIDRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
10 |
11 | assert.isTrue(validUUIDRegex.test(uuid), 'identifier should be a valid UUID');
12 | },
13 |
14 | pluralize: function () {
15 | assert.strictEqual(window.Utils.pluralize(1, 'todo'), 'todo', 'Word should be singular with singular count.');
16 | assert.strictEqual(window.Utils.pluralize(2, 'todo'), 'todos', 'Word should be plural with count greater than 1.');
17 | assert.strictEqual(window.Utils.pluralize(0, 'todo'), 'todos', 'Word should be plural with zero count.');
18 | },
19 |
20 | store: function () {
21 | window.localStorage.removeItem('test-store');
22 | var store = window.Utils.store('test-store');
23 | assert.strictEqual(store.length, 0, 'Creating a new store should result in an empty array.');
24 | store.push('anItem');
25 | window.Utils.store('test-store', store);
26 | assert.strictEqual(window.Utils.store('test-store').length, 1, 'Store should contain one item after adding one.');
27 | }
28 | });
29 | })();
30 |
--------------------------------------------------------------------------------
/parallel-example/README.md:
--------------------------------------------------------------------------------
1 | parallel-example
2 | =============
3 |
4 | ## Setup
5 |
6 | 1. Install node modules and intern
7 |
8 | ```
9 | npm install
10 | ```
11 |
12 | 2. Setup `tunnelOptions` and `capabilities`.
13 |
14 | ## Running tests
15 |
16 | * **Run tests in parallel**
17 |
18 | ```
19 | ./parallel.sh
20 | ```
21 |
--------------------------------------------------------------------------------
/parallel-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/parallel-example/intern.js:
--------------------------------------------------------------------------------
1 | // Small example file of config
2 | define({
3 | environments: [
4 | { browserName: 'chrome' }
5 | ],
6 |
7 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service
8 | maxConcurrency: 1,
9 |
10 | // A regular expression matching URLs to files that should not be included in code coverage analysis
11 | excludeInstrumentation: true
12 | });
--------------------------------------------------------------------------------
/parallel-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parallel-intern-example",
3 | "version": "0.2.0",
4 | "description": "An example showing a typical Angular 1.x application tested with Intern",
5 | "devDependencies": {
6 | "intern": "~3.4.2"
7 | },
8 | "scripts": {
9 | "test": "intern-runner config=tests/intern"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/parallel-example/parallel.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | datetime=$(date '+%Y-%m-%d_%H-%M-%S')
4 | declare -a suites=( \
5 | "sample1" \
6 | "sample2" \
7 | )
8 |
9 | arraylength=${#suites[@]};
10 |
11 | for (( i=0; i<${arraylength}; i++ ));
12 | do
13 | p=$(expr $i + 1);
14 | ./node_modules/.bin/intern-runner config=intern \
15 | functionalSuites=tests/functional/${suites[$i]} \
16 | proxyPort=900$p \
17 | proxyUrl=http://localhost:900$p/ &
18 | done;
19 |
--------------------------------------------------------------------------------
/parallel-example/tests/functional/sample1.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'require'
4 | ], function (
5 | registerSuite,
6 | require
7 | ) {
8 | var url = '../../index.html';
9 |
10 | registerSuite({
11 | name: 'Sample ',
12 |
13 | 'submit form': function () {
14 | return this.remote
15 | .get(require.toUrl(url))
16 | .sleep(5000);
17 | }
18 | });
19 | });
--------------------------------------------------------------------------------
/parallel-example/tests/functional/sample2.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'require'
4 | ], function (
5 | registerSuite,
6 | require
7 | ) {
8 | var url = '../../index.html';
9 |
10 | registerSuite({
11 | name: 'Sample 2',
12 |
13 | 'submit form': function () {
14 | return this.remote
15 | .get(require.toUrl(url))
16 | .sleep(5000);
17 | }
18 | });
19 | });
--------------------------------------------------------------------------------
/phantomjs-example/README.md:
--------------------------------------------------------------------------------
1 | phantomjs-example
2 | =============
3 |
4 | * Install PhantomJS: `npm install -g phantomjs`
5 | * Ensure that the directory containing the phantomjs executable is in your PATH
6 | * Run `npm install`
7 | * Run tests: `npm test`
8 |
--------------------------------------------------------------------------------
/phantomjs-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phantomjs-example",
3 | "version": "0.2.0",
4 | "scripts": {
5 | "test": "intern-runner config=tests/intern"
6 | },
7 | "devDependencies": {
8 | "intern": "~3.4.2"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/phantomjs-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | // Learn more about configuring this file at .
2 | // These default settings work OK for most people. The options that *must* be changed below are the
3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites.
4 | define({
5 | // Browsers to run integration testing against. Options that will be permutated are browserName, version, platform,
6 | // and platformVersion; any other capabilities options specified for an environment will be copied as-is. Note that
7 | // browser and platform names, and version number formats, may differ between cloud testing systems.
8 | environments: [ { browserName: 'phantomjs' } ],
9 |
10 | // Name of the tunnel class to use for WebDriver tests.
11 | // See for built-in options
12 | tunnel: 'SeleniumTunnel',
13 |
14 | // Unit test suite(s) to run in each browser
15 | suites: [ 'tests/test_example.js' ],
16 |
17 | // A regular expression matching URLs to files that should not be included in code coverage analysis. Set to `true`
18 | // to completely disable code coverage.
19 | excludeInstrumentation: true
20 | });
21 |
--------------------------------------------------------------------------------
/phantomjs-example/tests/test_example.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert'
4 | ], function (registerSuite, assert) {
5 | registerSuite({
6 | name: 'suite',
7 |
8 | 'example test': function () {
9 | assert.ok(true);
10 | }
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/react-enzyme-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"],
3 | "plugins": [
4 | "transform-object-rest-spread",
5 | "transform-react-jsx",
6 | "transform-class-properties"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/react-enzyme-example/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/react-enzyme-example/README.md:
--------------------------------------------------------------------------------
1 | react-enzyme-example
2 | ====================
3 |
4 | This example is based on the [React Redux example](https://github.com/reactjs/redux/tree/master/examples/todomvc).
5 |
6 | ## Setup
7 |
8 | 1. Install the JRE or JDK. This demo uses Selenium, which requires Java, to run WebDriver tests.
9 |
10 | 2. Install node modules
11 | ```
12 | npm install
13 | ```
14 |
15 | 3. Build the example (this is necessary for functional tests to work)
16 | ```
17 | $ npm run build
18 | ```
19 |
20 | ## Running tests
21 |
22 | **Unit tests in Node**
23 |
24 | $ npm test
25 |
26 | **WebDriver tests**
27 |
28 | $ npm test webdriver
29 |
30 | ## Notes
31 |
32 | In the `intern.json` file, there are separate `browserSuites` and `nodeSuites` properties. This is because some of the
33 | unit tests don't work in the browser, but all of them will work in Node.
34 |
--------------------------------------------------------------------------------
/react-enzyme-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "connectTimeout": 3000,
3 | "browser": {
4 | "suites": "tests/unit/reducers/todos.js",
5 | "loader": {
6 | "script": "systemjs",
7 | "options": {
8 | "map": {
9 | "plugin-babel": "node_modules/systemjs-plugin-babel/plugin-babel.js",
10 | "systemjs-babel-build": "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js"
11 | },
12 | "packages": {
13 | "src": {
14 | "defaultExtension": "js"
15 | }
16 | },
17 | "transpiler": "plugin-babel"
18 | }
19 | }
20 | },
21 | "node": {
22 | "suites": "tests/unit/**/*.js",
23 | "plugins": [
24 | "tests/build_check.js",
25 | "node_modules/babel-register/lib/node.js"
26 | ]
27 | },
28 | "instrumenterOptions": {
29 | "esModules": true
30 | },
31 | "functionalSuites": "tests/functional/**/*.js",
32 | "capabilities": {
33 | "fixSessionCapabilities": "no-detect"
34 | },
35 | "environments": "chrome",
36 | "filterErrorStack": true
37 | }
38 |
--------------------------------------------------------------------------------
/react-enzyme-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "react-enzyme-example",
4 | "version": "0.1.0",
5 | "description": "An example showing how to use React and Enzyme with Intern",
6 | "dependencies": {
7 | "babel-core": "~6.23.0",
8 | "babel-plugin-transform-class-properties": "~6.23.0",
9 | "babel-plugin-transform-react-jsx": "~6.23.0",
10 | "babel-plugin-transform-object-rest-spread": "~6.23.0",
11 | "babel-preset-env": "~1.3.3",
12 | "classnames": "~2.2.5",
13 | "prop-types": "~15.5.0",
14 | "react": "~15.5.0",
15 | "react-dom": "~15.5.0",
16 | "react-redux": "~5.0.0",
17 | "redux": "~3.6.0",
18 | "todomvc-app-css": "~2.0.6"
19 | },
20 | "devDependencies": {
21 | "enzyme": "~2.8.0",
22 | "intern": "file:../../intern/_build/src/intern-4.0.0-pre.tgz",
23 | "react-addons-test-utils": "~15.5.0",
24 | "react-scripts": "~0.9.3",
25 | "react-test-renderer": "~15.5.0",
26 | "serve": "~5.1.1",
27 | "systemjs": "~0.20.12",
28 | "systemjs-plugin-babel": "~0.0.21"
29 | },
30 | "scripts": {
31 | "build": "react-scripts build",
32 | "start": "serve build",
33 | "clean": "rm -rf build",
34 | "test": "intern"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/react-enzyme-example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redux TodoMVC Example
7 |
8 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes'
2 |
3 | export const addTodo = text => ({ type: types.ADD_TODO, text })
4 | export const deleteTodo = id => ({ type: types.DELETE_TODO, id })
5 | export const editTodo = (id, text) => ({ type: types.EDIT_TODO, id, text })
6 | export const completeTodo = id => ({ type: types.COMPLETE_TODO, id })
7 | export const completeAll = () => ({ type: types.COMPLETE_ALL })
8 | export const clearCompleted = () => ({ type: types.CLEAR_COMPLETED })
9 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import classnames from 'classnames'
4 | import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'
5 |
6 | const FILTER_TITLES = {
7 | [SHOW_ALL]: 'All',
8 | [SHOW_ACTIVE]: 'Active',
9 | [SHOW_COMPLETED]: 'Completed'
10 | }
11 |
12 | export default class Footer extends Component {
13 | static propTypes = {
14 | completedCount: PropTypes.number.isRequired,
15 | activeCount: PropTypes.number.isRequired,
16 | filter: PropTypes.string.isRequired,
17 | onClearCompleted: PropTypes.func.isRequired,
18 | onShow: PropTypes.func.isRequired
19 | }
20 |
21 | renderTodoCount() {
22 | const { activeCount } = this.props
23 | const itemWord = activeCount === 1 ? 'item' : 'items'
24 |
25 | return (
26 |
27 | {activeCount || 'No'} {itemWord} left
28 |
29 | )
30 | }
31 |
32 | renderFilterLink(filter) {
33 | const title = FILTER_TITLES[filter]
34 | const { filter: selectedFilter, onShow } = this.props
35 |
36 | return (
37 | onShow(filter)}>
40 | {title}
41 |
42 | )
43 | }
44 |
45 | renderClearButton() {
46 | const { completedCount, onClearCompleted } = this.props
47 | if (completedCount > 0) {
48 | return (
49 |
53 | )
54 | }
55 | }
56 |
57 | render() {
58 | return (
59 |
70 | )
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import TodoTextInput from './TodoTextInput'
4 |
5 | export default class Header extends Component {
6 | static propTypes = {
7 | addTodo: PropTypes.func.isRequired
8 | }
9 |
10 | handleSave = text => {
11 | if (text.length !== 0) {
12 | this.props.addTodo(text)
13 | }
14 | }
15 |
16 | render() {
17 | return (
18 |
24 | )
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/components/MainSection.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import TodoItem from './TodoItem'
4 | import Footer from './Footer'
5 | import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'
6 |
7 | const TODO_FILTERS = {
8 | [SHOW_ALL]: () => true,
9 | [SHOW_ACTIVE]: todo => !todo.completed,
10 | [SHOW_COMPLETED]: todo => todo.completed
11 | }
12 |
13 | export default class MainSection extends Component {
14 | static propTypes = {
15 | todos: PropTypes.array.isRequired,
16 | actions: PropTypes.object.isRequired
17 | }
18 |
19 | state = { filter: SHOW_ALL }
20 |
21 | handleClearCompleted = () => {
22 | this.props.actions.clearCompleted()
23 | }
24 |
25 | handleShow = filter => {
26 | this.setState({ filter })
27 | }
28 |
29 | renderToggleAll(completedCount) {
30 | const { todos, actions } = this.props
31 | if (todos.length > 0) {
32 | return (
33 |
37 | )
38 | }
39 | }
40 |
41 | renderFooter(completedCount) {
42 | const { todos } = this.props
43 | const { filter } = this.state
44 | const activeCount = todos.length - completedCount
45 |
46 | if (todos.length) {
47 | return (
48 |
53 | )
54 | }
55 | }
56 |
57 | render() {
58 | const { todos, actions } = this.props
59 | const { filter } = this.state
60 |
61 | const filteredTodos = todos.filter(TODO_FILTERS[filter])
62 | const completedCount = todos.reduce((count, todo) =>
63 | todo.completed ? count + 1 : count,
64 | 0
65 | )
66 |
67 | return (
68 |
69 | {this.renderToggleAll(completedCount)}
70 |
71 | {filteredTodos.map(todo =>
72 |
73 | )}
74 |
75 | {this.renderFooter(completedCount)}
76 |
77 | )
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import classnames from 'classnames'
4 | import TodoTextInput from './TodoTextInput'
5 |
6 | export default class TodoItem extends Component {
7 | static propTypes = {
8 | todo: PropTypes.object.isRequired,
9 | editTodo: PropTypes.func.isRequired,
10 | deleteTodo: PropTypes.func.isRequired,
11 | completeTodo: PropTypes.func.isRequired
12 | }
13 |
14 | state = {
15 | editing: false
16 | }
17 |
18 | handleDoubleClick = () => {
19 | this.setState({ editing: true })
20 | }
21 |
22 | handleSave = (id, text) => {
23 | if (text.length === 0) {
24 | this.props.deleteTodo(id)
25 | } else {
26 | this.props.editTodo(id, text)
27 | }
28 | this.setState({ editing: false })
29 | }
30 |
31 | render() {
32 | const { todo, completeTodo, deleteTodo } = this.props
33 |
34 | let element
35 | if (this.state.editing) {
36 | element = (
37 | this.handleSave(todo.id, text)} />
40 | )
41 | } else {
42 | element = (
43 |
44 | completeTodo(todo.id)} />
48 |
51 |
54 | )
55 | }
56 |
57 | return (
58 | -
62 | {element}
63 |
64 | )
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/components/TodoTextInput.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import classnames from 'classnames'
4 |
5 | export default class TodoTextInput extends Component {
6 | static propTypes = {
7 | onSave: PropTypes.func.isRequired,
8 | text: PropTypes.string,
9 | placeholder: PropTypes.string,
10 | editing: PropTypes.bool,
11 | newTodo: PropTypes.bool
12 | }
13 |
14 | state = {
15 | text: this.props.text || ''
16 | }
17 |
18 | handleSubmit = e => {
19 | const text = e.target.value.trim()
20 | if (e.which === 13) {
21 | this.props.onSave(text)
22 | if (this.props.newTodo) {
23 | this.setState({ text: '' })
24 | }
25 | }
26 | }
27 |
28 | handleChange = e => {
29 | this.setState({ text: e.target.value })
30 | }
31 |
32 | handleBlur = e => {
33 | if (!this.props.newTodo) {
34 | this.props.onSave(e.target.value)
35 | }
36 | }
37 |
38 | render() {
39 | return (
40 |
52 | )
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_TODO = 'ADD_TODO'
2 | export const DELETE_TODO = 'DELETE_TODO'
3 | export const EDIT_TODO = 'EDIT_TODO'
4 | export const COMPLETE_TODO = 'COMPLETE_TODO'
5 | export const COMPLETE_ALL = 'COMPLETE_ALL'
6 | export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
7 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/constants/TodoFilters.js:
--------------------------------------------------------------------------------
1 | export const SHOW_ALL = 'show_all'
2 | export const SHOW_COMPLETED = 'show_completed'
3 | export const SHOW_ACTIVE = 'show_active'
4 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/containers/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { bindActionCreators } from 'redux'
4 | import { connect } from 'react-redux'
5 | import Header from '../components/Header'
6 | import MainSection from '../components/MainSection'
7 | import * as TodoActions from '../actions'
8 |
9 | const App = ({todos, actions}) => (
10 |
11 |
12 |
13 |
14 | )
15 |
16 | App.propTypes = {
17 | todos: PropTypes.array.isRequired,
18 | actions: PropTypes.object.isRequired
19 | }
20 |
21 | const mapStateToProps = state => ({
22 | todos: state.todos
23 | })
24 |
25 | const mapDispatchToProps = dispatch => ({
26 | actions: bindActionCreators(TodoActions, dispatch)
27 | })
28 |
29 | export default connect(
30 | mapStateToProps,
31 | mapDispatchToProps
32 | )(App)
33 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { createStore } from 'redux'
4 | import { Provider } from 'react-redux'
5 | import App from './containers/App'
6 | import reducer from './reducers'
7 | import 'todomvc-app-css/index.css'
8 |
9 | const store = createStore(reducer)
10 |
11 | render(
12 |
13 |
14 | ,
15 | document.getElementById('root')
16 | )
17 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import todos from './todos'
3 |
4 | const rootReducer = combineReducers({
5 | todos
6 | })
7 |
8 | export default rootReducer
9 |
--------------------------------------------------------------------------------
/react-enzyme-example/src/reducers/todos.js:
--------------------------------------------------------------------------------
1 | import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes'
2 |
3 | const initialState = [
4 | {
5 | text: 'Use Redux',
6 | completed: false,
7 | id: 0
8 | }
9 | ]
10 |
11 | export default function todos(state = initialState, action) {
12 | switch (action.type) {
13 | case ADD_TODO:
14 | return [
15 | {
16 | id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
17 | completed: false,
18 | text: action.text
19 | },
20 | ].concat(state)
21 |
22 | case DELETE_TODO:
23 | return state.filter(todo =>
24 | todo.id !== action.id
25 | )
26 |
27 | case EDIT_TODO:
28 | return state.map(todo => {
29 | if (todo.id === action.id) {
30 | return Object.assign({}, todo, { text: action.text })
31 | } else {
32 | return todo
33 | }
34 | })
35 |
36 | case COMPLETE_TODO:
37 | return state.map(todo => {
38 | if (todo.id === action.id) {
39 | return Object.assign({}, todo, { completed: !todo.completed })
40 | } else {
41 | return todo
42 | }
43 | })
44 |
45 | case COMPLETE_ALL:
46 | const areAllMarked = state.every(todo => todo.completed)
47 | return state.map(todo => {
48 | return Object.assign({}, todo, { completed: !areAllMarked })
49 | })
50 |
51 | case CLEAR_COMPLETED:
52 | return state.filter(todo => todo.completed === false)
53 |
54 | default:
55 | return state
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/react-enzyme-example/tests/build_check.js:
--------------------------------------------------------------------------------
1 | /* global intern */
2 | var existsSync = require('fs').existsSync;
3 | var serve = require('serve');
4 | var server;
5 |
6 | intern.on('beforeRun', function () {
7 | var config = intern.config;
8 |
9 | if (!existsSync('build')) {
10 | throw new Error('Project must be built first');
11 | }
12 | else if (config.environments.length > 0) {
13 | server = serve('build', {
14 | port: 10543,
15 | silent: true
16 | });
17 | }
18 | });
19 |
20 | intern.on('afterRun', function () {
21 | server.stop();
22 | });
23 |
--------------------------------------------------------------------------------
/react-enzyme-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | const { describe, it } = intern.getPlugin('interface.bdd');
2 | const expect = intern.getPlugin('chai.expect');
3 |
4 | describe('Todo (functional)', suite => {
5 | it('should let users add items', function () {
6 | return this.remote
7 | .setFindTimeout(5000)
8 | .get('http://localhost:10543/index.html')
9 | .findByCssSelector('input.new-todo')
10 | .click()
11 | .pressKeys('Task 1')
12 | .pressKeys('\n')
13 | .pressKeys('Task 2')
14 | .pressKeys('\n')
15 | .pressKeys('Task 3')
16 | .getSpecAttribute('value')
17 | .then(function (val) {
18 | expect(val).to.include('Task 3');
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/react-enzyme-example/tests/unit/actions/index.js:
--------------------------------------------------------------------------------
1 | import * as actions from '../../../src/actions/index';
2 | import * as types from '../../../src/constants/ActionTypes'
3 |
4 | const { describe, it } = intern.getPlugin('interface.bdd');
5 | const expect = intern.getPlugin('chai.expect');
6 |
7 | describe('todo actions', () => {
8 | it('addTodo should create ADD_TODO action', () => {
9 | expect(actions.addTodo('Use Redux')).to.eql({
10 | type: types.ADD_TODO,
11 | text: 'Use Redux'
12 | })
13 | })
14 |
15 | it('deleteTodo should create DELETE_TODO action', () => {
16 | expect(actions.deleteTodo(1)).to.eql({
17 | type: types.DELETE_TODO,
18 | id: 1
19 | })
20 | })
21 |
22 | it('editTodo should create EDIT_TODO action', () => {
23 | expect(actions.editTodo(1, 'Use Redux everywhere')).to.eql({
24 | type: types.EDIT_TODO,
25 | id: 1,
26 | text: 'Use Redux everywhere'
27 | })
28 | })
29 |
30 | it('completeTodo should create COMPLETE_TODO action', () => {
31 | expect(actions.completeTodo(1)).to.eql({
32 | type: types.COMPLETE_TODO,
33 | id: 1
34 | })
35 | })
36 |
37 | it('completeAll should create COMPLETE_ALL action', () => {
38 | expect(actions.completeAll()).to.eql({
39 | type: types.COMPLETE_ALL
40 | })
41 | })
42 |
43 | it('clearCompleted should create CLEAR_COMPLETED action', () => {
44 | expect(actions.clearCompleted()).to.eql({
45 | type: types.CLEAR_COMPLETED
46 | })
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/react-enzyme-example/tests/unit/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow, render } from 'enzyme'
3 | import Header from '../../../src/components/Header'
4 | import TodoTextInput from '../../../src/components/TodoTextInput'
5 |
6 | const { describe, it } = intern.getPlugin('interface.bdd');
7 | const expect = intern.getPlugin('chai.expect');
8 |
9 | const setup = () => {
10 | const addTodo = () => { addTodo.called = true; }
11 | addTodo.called = false;
12 | return addTodo;
13 | }
14 |
15 | describe('components', () => {
16 | describe('Header', () => {
17 | it('should render correctly', () => {
18 | const addTodo = setup();
19 | const component = shallow();
20 |
21 | expect(component.type()).to.equal('header');
22 | expect(component.prop('className')).to.equal('header');
23 |
24 | const h1 = component.childAt(0);
25 | const input = component.childAt(1);
26 |
27 | expect(h1.type()).to.equal('h1');
28 | expect(h1.prop('children')).to.equal('todos');
29 |
30 | expect(input.type()).to.equal(TodoTextInput);
31 | expect(input.prop('newTodo')).to.equal(true);
32 | expect(input.prop('placeholder')).to.equal('What needs to be done?');
33 | });
34 |
35 | it('should call addTodo if length of text is greater than 0', () => {
36 | const addTodo = setup();
37 | const component = shallow();
38 |
39 | const input = component.childAt(1);
40 | input.prop('onSave')('');
41 |
42 | // Use `component.instance().props` to get at `addTodo` since `addTodo` isn't a rendered property
43 | expect(component.instance().props.addTodo.called).to.not.be.true;
44 |
45 | input.prop('onSave')('Use Redux');
46 | expect(component.instance().props.addTodo.called).to.be.true;
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/react-example/README.md:
--------------------------------------------------------------------------------
1 | react-example
2 | =============
3 |
4 | ## Setup
5 |
6 | 1. Install the JRE or JDK
7 | This demo runs with local Selenium, which Intern will automatically install.
8 |
9 | 2. Install intern command line interface
10 |
11 | ```
12 | npm install -g intern-cli
13 | ```
14 |
15 | 3. Install node modules and intern
16 |
17 | ```
18 | npm install
19 | ```
20 |
21 | ## Running tests
22 |
23 | * **Local browser tests**
24 |
25 | ```
26 | intern serve
27 | ```
28 |
29 | Navigate to `http://localhost:9000/node_modules/intern/client.html?config=tests/intern.js`.
30 |
31 | * **Remote node / browser tests**
32 |
33 | ```
34 | intern run --webdriver
35 | ```
36 |
--------------------------------------------------------------------------------
/react-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React • TodoMVC
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/react-example/js/footer.jsx:
--------------------------------------------------------------------------------
1 | /*jshint quotmark:false */
2 | /*jshint white:false */
3 | /*jshint trailing:false */
4 | /*jshint newcap:false */
5 | /*global React */
6 | var app = app || {};
7 |
8 | (function () {
9 | 'use strict';
10 |
11 | app.TodoFooter = React.createClass({
12 | render: function () {
13 | var activeTodoWord = app.Utils.pluralize(this.props.count, 'item');
14 | var clearButton = null;
15 |
16 | if (this.props.completedCount > 0) {
17 | clearButton = (
18 |
23 | );
24 | }
25 |
26 | // React idiom for shortcutting to `classSet` since it'll be used often
27 | var cx = React.addons.classSet;
28 | var nowShowing = this.props.nowShowing;
29 | return (
30 |
61 | );
62 | }
63 | });
64 | })();
65 |
--------------------------------------------------------------------------------
/react-example/js/todoItem.jsx:
--------------------------------------------------------------------------------
1 | /*jshint quotmark: false */
2 | /*jshint white: false */
3 | /*jshint trailing: false */
4 | /*jshint newcap: false */
5 | /*global React */
6 | var app = app || {};
7 |
8 | (function () {
9 | 'use strict';
10 |
11 | var ESCAPE_KEY = 27;
12 | var ENTER_KEY = 13;
13 |
14 | app.TodoItem = React.createClass({
15 | handleSubmit: function (event) {
16 | var val = this.state.editText.trim();
17 | if (val) {
18 | this.props.onSave(val);
19 | this.setState({editText: val});
20 | } else {
21 | this.props.onDestroy();
22 | }
23 | },
24 |
25 | handleEdit: function () {
26 | // react optimizes renders by batching them. This means you can't call
27 | // parent's `onEdit` (which in this case triggeres a re-render), and
28 | // immediately manipulate the DOM as if the rendering's over. Put it as a
29 | // callback. Refer to app.jsx' `edit` method
30 | this.props.onEdit(function () {
31 | var node = this.refs.editField.getDOMNode();
32 | node.focus();
33 | node.setSelectionRange(node.value.length, node.value.length);
34 | }.bind(this));
35 | this.setState({editText: this.props.todo.title});
36 | },
37 |
38 | handleKeyDown: function (event) {
39 | if (event.which === ESCAPE_KEY) {
40 | this.setState({editText: this.props.todo.title});
41 | this.props.onCancel(event);
42 | } else if (event.which === ENTER_KEY) {
43 | this.handleSubmit(event);
44 | }
45 | },
46 |
47 | handleChange: function (event) {
48 | this.setState({editText: event.target.value});
49 | },
50 |
51 | getInitialState: function () {
52 | return {editText: this.props.todo.title};
53 | },
54 |
55 | /**
56 | * This is a completely optional performance enhancement that you can
57 | * implement on any React component. If you were to delete this method
58 | * the app would still work correctly (and still be very performant!), we
59 | * just use it as an example of how little code it takes to get an order
60 | * of magnitude performance improvement.
61 | */
62 | shouldComponentUpdate: function (nextProps, nextState) {
63 | return (
64 | nextProps.todo !== this.props.todo ||
65 | nextProps.editing !== this.props.editing ||
66 | nextState.editText !== this.state.editText
67 | );
68 | },
69 |
70 | render: function () {
71 | return (
72 | -
76 |
77 |
83 |
86 |
87 |
88 |
96 |
97 | );
98 | }
99 | });
100 | })();
101 |
--------------------------------------------------------------------------------
/react-example/js/todoModel.js:
--------------------------------------------------------------------------------
1 | /*jshint quotmark:false */
2 | /*jshint white:false */
3 | /*jshint trailing:false */
4 | /*jshint newcap:false */
5 | var app = app || {};
6 |
7 | (function () {
8 | 'use strict';
9 |
10 | var Utils = app.Utils;
11 | // Generic "model" object. You can use whatever
12 | // framework you want. For this application it
13 | // may not even be worth separating this logic
14 | // out, but we do this to demonstrate one way to
15 | // separate out parts of your application.
16 | app.TodoModel = function (key) {
17 | this.key = key;
18 | this.todos = Utils.store(key);
19 | this.onChanges = [];
20 | };
21 |
22 | app.TodoModel.prototype.subscribe = function (onChange) {
23 | this.onChanges.push(onChange);
24 | };
25 |
26 | app.TodoModel.prototype.inform = function () {
27 | Utils.store(this.key, this.todos);
28 | this.onChanges.forEach(function (cb) { cb(); });
29 | };
30 |
31 | app.TodoModel.prototype.addTodo = function (title) {
32 | this.todos = this.todos.concat({
33 | id: Utils.uuid(),
34 | title: title,
35 | completed: false
36 | });
37 |
38 | this.inform();
39 | };
40 |
41 | app.TodoModel.prototype.toggleAll = function (checked) {
42 | // Note: it's usually better to use immutable data structures since they're
43 | // easier to reason about and React works very well with them. That's why
44 | // we use map() and filter() everywhere instead of mutating the array or
45 | // todo items themselves.
46 | this.todos = this.todos.map(function (todo) {
47 | return Utils.extend({}, todo, {completed: checked});
48 | });
49 |
50 | this.inform();
51 | };
52 |
53 | app.TodoModel.prototype.toggle = function (todoToToggle) {
54 | this.todos = this.todos.map(function (todo) {
55 | return todo !== todoToToggle ?
56 | todo :
57 | Utils.extend({}, todo, {completed: !todo.completed});
58 | });
59 |
60 | this.inform();
61 | };
62 |
63 | app.TodoModel.prototype.destroy = function (todo) {
64 | this.todos = this.todos.filter(function (candidate) {
65 | return candidate !== todo;
66 | });
67 |
68 | this.inform();
69 | };
70 |
71 | app.TodoModel.prototype.save = function (todoToSave, text) {
72 | this.todos = this.todos.map(function (todo) {
73 | return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text});
74 | });
75 |
76 | this.inform();
77 | };
78 |
79 | app.TodoModel.prototype.clearCompleted = function () {
80 | this.todos = this.todos.filter(function (todo) {
81 | return !todo.completed;
82 | });
83 |
84 | this.inform();
85 | };
86 |
87 | })();
88 |
--------------------------------------------------------------------------------
/react-example/js/utils.js:
--------------------------------------------------------------------------------
1 | var app = app || {};
2 |
3 | (function () {
4 | 'use strict';
5 |
6 | app.Utils = {
7 | uuid: function () {
8 | /*jshint bitwise:false */
9 | var i, random;
10 | var uuid = '';
11 |
12 | for (i = 0; i < 32; i++) {
13 | random = Math.random() * 16 | 0;
14 | if (i === 8 || i === 12 || i === 16 || i === 20) {
15 | uuid += '-';
16 | }
17 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
18 | .toString(16);
19 | }
20 |
21 | return uuid;
22 | },
23 |
24 | pluralize: function (count, word) {
25 | return count === 1 ? word : word + 's';
26 | },
27 |
28 | store: function (namespace, data) {
29 | if (data) {
30 | return localStorage.setItem(namespace, JSON.stringify(data));
31 | }
32 |
33 | var store = localStorage.getItem(namespace);
34 | return (store && JSON.parse(store)) || [];
35 | },
36 |
37 | extend: function () {
38 | var newObj = {};
39 | for (var i = 0; i < arguments.length; i++) {
40 | var obj = arguments[i];
41 | for (var key in obj) {
42 | if (obj.hasOwnProperty(key)) {
43 | newObj[key] = obj[key];
44 | }
45 | }
46 | }
47 | return newObj;
48 | }
49 | };
50 | })();
51 |
--------------------------------------------------------------------------------
/react-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-intern-example",
3 | "private": true,
4 | "version": "0.2.0",
5 | "description": "An example showing a typical React application tested with Intern",
6 | "dependencies": {
7 | "director": "~1.2.0",
8 | "react": "~0.12.0",
9 | "todomvc-app-css": "~1.0.0",
10 | "todomvc-common": "~1.0.1"
11 | },
12 | "devDependencies": {
13 | "intern": "~3.4.2"
14 | },
15 | "scripts": {
16 | "test": "intern-runner config=tests/intern"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/react-example/tests/all.js:
--------------------------------------------------------------------------------
1 | define([
2 | './todoModel'
3 | ], function () {});
4 |
--------------------------------------------------------------------------------
/react-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert',
4 | 'require'
5 | ], function (registerSuite, assert, require) {
6 | var url = '../../index.html';
7 |
8 | registerSuite({
9 | name: 'Todo (functional)',
10 |
11 | 'submit form': function () {
12 | return this.remote
13 | .get(require.toUrl(url))
14 | .findById('new-todo')
15 | .click()
16 | .pressKeys('Task 1')
17 | .pressKeys('\n')
18 | .pressKeys('Task 2')
19 | .pressKeys('\n')
20 | .pressKeys('Task 3')
21 | .getProperty('value')
22 | .then(function (val) {
23 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
24 | });
25 | }
26 | });
27 | });
--------------------------------------------------------------------------------
/react-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | // Learn more about configuring this file at .
2 | // These default settings work OK for most people. The options that *must* be changed below are the
3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites.
4 | define({
5 | // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the
6 | // specified browser environments in the `environments` array below as well. See
7 | // for links to the different capabilities options for
8 | // different services.
9 | //
10 | // Note that the `build` capability will be filled in with the current commit ID or build tag from the CI
11 | // environment automatically
12 | capabilities: {},
13 |
14 | // Browsers to run integration testing against. Options that will be permutated are browserName, version, platform,
15 | // and platformVersion; any other capabilities options specified for an environment will be copied as-is. Note that
16 | // browser and platform names, and version number formats, may differ between cloud testing systems.
17 | environments: [ { browserName: 'chrome' } ],
18 |
19 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service
20 | maxConcurrency: 3,
21 |
22 | // Name of the tunnel class to use for WebDriver tests.
23 | // See for built-in options
24 | tunnel: 'SeleniumTunnel',
25 |
26 | // Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use
27 | // can be used here.
28 | // If you want to use a different loader than the default loader, see
29 | // for more information.
30 | loaderOptions: {
31 | // Packages that should be registered with the loader in each testing environment
32 | packages: [
33 | { name: 'todo', location: 'js' },
34 | { name: 'react', location: 'node_modules/react' },
35 | { name: 'director', location: 'node_modules/director' },
36 | { name: 'todomvc-app-css', location: 'node_modules/todomvc-app-css' },
37 | { name: 'todomvc-app-common', location: 'node_modules/todomvc-app-common' }
38 | ]
39 | },
40 |
41 | // Unit test suite(s) to run in each browser
42 | suites: [ 'tests/all' ],
43 |
44 | // Functional test suite(s) to execute against each browser once unit tests are completed
45 | functionalSuites: [ 'tests/functional/Todo' ],
46 |
47 | // A regular expression matching URLs to files that should not be included in code coverage analysis. Set to `true`
48 | // to completely disable code coverage.
49 | excludeInstrumentation: /^(?:tests|node_modules)\//
50 | });
51 |
--------------------------------------------------------------------------------
/react-example/tests/support/jsx.js:
--------------------------------------------------------------------------------
1 | // Inspired by https://theintern.github.io/intern/#testing-other-module
2 |
3 | define([
4 | 'intern/dojo/request',
5 | 'react/JSXTransformer',
6 | 'react/react-with-addons'
7 | ], function (request, transformer, react) {
8 | /**
9 | * React has AMD support so when require is present it will behave as a module
10 | * The react example however expects a global React so we need to put it back
11 | * into global space.
12 | */
13 | function globalizeReact() {
14 | var global = (function () {
15 | return this;
16 | })();
17 | global.React = global.React || react;
18 | }
19 |
20 | return {
21 | /**
22 | * A function that is called to load a resource.
23 | *
24 | * @param name The name of the resource to load.
25 | * @param req A local "require" function to use to load other modules.
26 | * @param onload A function to call with the value for name. This tells the loader that the plugin is done
27 | * loading the resource. onload.error() can be called, passing an error object to it, if the plugin
28 | * detects an error condition that means the resource will fail to load correctly.
29 | */
30 | load: function (name, req, onload) {
31 | globalizeReact();
32 |
33 | request(req.toUrl(name)).then(function (sourceCode) {
34 | // Compile the JSX source into JavaScript code
35 | var javascriptCode = transformer.transform(sourceCode).code;
36 |
37 | // Execute the compiled function. In this case the example code
38 | // puts things into the global space so it needs to be run in a script tag.
39 | var codeNode = document.createTextNode(javascriptCode);
40 | var node = document.createElement('script');
41 | node.type = 'text/javascript';
42 | node.appendChild(codeNode);
43 | document.head.appendChild(node);
44 | onload();
45 | });
46 | }
47 | };
48 | });
49 |
--------------------------------------------------------------------------------
/react-example/tests/todoModel.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert',
4 | 'intern/order!todo/utils',
5 | 'intern/order!todo/todoModel'
6 | ], function (registerSuite, assert, todoModel) {
7 | registerSuite({
8 | name: 'todoModel',
9 |
10 | beforeEach: function () {
11 | localStorage.clear();
12 | },
13 |
14 | 'default data': function () {
15 | var emptyModel = new app.TodoModel('foo');
16 | assert.strictEqual(emptyModel.todos.length, 0, 'Todos array should default to an empty array.');
17 | },
18 |
19 | 'complete (empty model)': function () {
20 | var emptyModel = new app.TodoModel('bar');
21 | emptyModel.addTodo('item1');
22 | emptyModel.addTodo('item2');
23 | assert.strictEqual(emptyModel.todos.length, 2, 'Todos array should have two items.');
24 | assert.strictEqual(emptyModel.todos.filter(function (todo) { return !!todo.completed; } ).length, 0, 'Prepopulated model todos should not be complete');
25 | emptyModel.toggleAll(true);
26 | assert.strictEqual(emptyModel.todos.filter(function (todo) { return !!todo.completed; } ).length, 2, 'Complete count should change when item is manually toggled');
27 | }
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/run_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo Running All Examples
4 |
5 | # angular
6 | cd angularjs-example
7 | npm install
8 | npm test
9 | cd ..
10 |
11 | # backbone
12 | cd backbone-example
13 | npm install
14 | npm test
15 | cd ..
16 |
17 | # dojo-example
18 | cd dojo-example
19 | npm install
20 | npm test
21 | cd ..
22 |
23 | # grunt-example
24 | cd grunt-example
25 | npm install
26 | npm test
27 | cd ..
28 |
29 | # jquery-example
30 | cd jquery-example
31 | npm install
32 | npm test
33 | cd ..
34 |
35 | # travis-example
36 | cd travis-ci-example
37 | npm install
38 | npm test
39 | cd ..
40 |
--------------------------------------------------------------------------------
/travis-ci-example/README.md:
--------------------------------------------------------------------------------
1 | travis-ci-example
2 | =============
3 |
4 | ## Setup
5 |
6 | * The easiest way to try this yourself is to probably fork this example
7 | * Go to [https://travis-ci.org/profile](https://travis-ci.org/profile) and switch on the repository you would like to use
8 | with [Travis CI](https://travis-ci.org/)
9 | * Adjust `.travis.yml`
10 | * Make a commit
11 |
12 | You can find more information on [continuous integration with Intern](https://github.com/theintern/intern/wiki/Travis-CI-integration) in the wiki.
13 |
--------------------------------------------------------------------------------
/travis-ci-example/app/App.js:
--------------------------------------------------------------------------------
1 | var app = {
2 | version: 1
3 | };
4 |
--------------------------------------------------------------------------------
/travis-ci-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "travis-ci-example",
3 | "description": "An example of using Travis CI with Intern",
4 | "version": "0.2.0",
5 | "private": true,
6 | "scripts": {
7 | "test": "intern-client config=tests/intern"
8 | },
9 | "devDependencies": {
10 | "intern": "~3.4.2"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/travis-ci-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | // Learn more about configuring this file at .
2 | // These default settings work OK for most people. The options that *must* be changed below are the
3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites
4 | define({
5 | // Functional test suite(s) to execute against each browser once unit tests are completed
6 | suites: ['tests/lib/demo'],
7 |
8 | // A regular expression matching URLs to files that should not be included in code coverage analysis. Set to `true`
9 | // to completely disable code coverage.
10 | excludeInstrumentation: true
11 | });
12 |
--------------------------------------------------------------------------------
/travis-ci-example/tests/lib/demo.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!tdd',
3 | 'intern/chai!assert',
4 | '../../app/App'
5 | ], function (tdd, assert) {
6 | with (tdd) {
7 | suite('demo', function () {
8 | test('#example', function () {
9 | // app comes from '../../app/App'
10 | assert.equal(app.version, 1);
11 | });
12 | });
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/typescript-example/.gitignore:
--------------------------------------------------------------------------------
1 | tests/**/*.js
2 | src/**/*.js
3 |
--------------------------------------------------------------------------------
/typescript-example/README.md:
--------------------------------------------------------------------------------
1 | # typescript-example
2 |
3 | This example uses Intern to test a jQuery + Backbone TodoMVC app written in TypeScript.
4 |
5 | ## Setup
6 |
7 | 1. Install the JRE or JDK
8 | This demo runs with local Selenium, which Intern will automatically install.
9 |
10 | 2. Install node modules
11 |
12 | ```
13 | npm install
14 | ```
15 |
16 | ## Running tests
17 |
18 | * **From a browser**
19 |
20 | ```
21 | npm start
22 | ```
23 |
24 | Navigate to `http://localhost:9000/__intern/`
25 |
26 | * **Using WebDriver**
27 |
28 | ```
29 | npm test
30 | ```
31 |
32 | The `npm test` command will run tests in Chrome by default. The test config in this project contains convenience configurations for other browsers as well.
33 |
34 | ```
35 | npm test config=@edge
36 | npm test config=@firefox
37 | npm test config=@ie
38 | npm test config=@safari
39 | ```
40 |
--------------------------------------------------------------------------------
/typescript-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Backbone.js • TodoMVC
7 |
8 |
9 |
10 |
11 |
23 |
28 |
36 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/typescript-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | // Test in Chrome by default.
3 | "environments": "chrome",
4 |
5 | // Resource properties such as 'suites' must point to the built files
6 | // rather than the TypeScript sources.
7 | "suites": "tests/unit/**/*.js",
8 | "functionalSuites": "tests/functional/**/*.js",
9 | "coverage": "src/**/*.js",
10 |
11 | "browser": {
12 | // SystemJS is needed to load the application modules in the browser
13 | "loader": "systemjs",
14 |
15 | // The application's SystemJS config is loaded here as it's necessary
16 | // when loading individual application modules. The 'useLoader' flag
17 | // indicates that the file should be loaded using the SystemJS loader.
18 | "plugins": { "script": "src/config.js", "useLoader": true }
19 | },
20 |
21 | // Convenience configs for using various browsers
22 | "configs": {
23 | "firefox": {
24 | "environments": "firefox",
25 | "tunnelOptions": {
26 | "drivers": ["firefox"]
27 | }
28 | },
29 | "ie": {
30 | "environments": "internet explorer",
31 | "tunnelOptions": {
32 | "drivers": ["ie"]
33 | }
34 | },
35 | "edge": {
36 | "environments": "MicrosoftEdge",
37 | "tunnelOptions": {
38 | "drivers": ["edge"]
39 | }
40 | },
41 | "safari": {
42 | "environments": {
43 | "browser": "safari",
44 | // This disables Intern's browser feature detection, which can
45 | // make Safari 11 unresponsive.
46 | "fixSessionCapabilities": "no-detect"
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/typescript-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript-example",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "An example testing a simple TypeScript app with Intern",
6 | "scripts": {
7 | "test": "intern",
8 | "start": "intern serveOnly",
9 | "build": "tsc && tsc -p tests",
10 | "clean": "del 'src/**/*.js' 'tests/**/*.js'"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "@types/backbone": "~1.4.1",
16 | "@types/backbone.localstorage": "~1.0.32",
17 | "@types/jquery": "~3.3.31",
18 | "@types/systemjs": "~0.20.6",
19 | "backbone": "~1.4.0",
20 | "backbone.localstorage": "~2.0.2",
21 | "del-cli": "~3.0.0",
22 | "intern": "~4.7.1",
23 | "jquery": "~3.5.0",
24 | "systemjs": "~0.20.19",
25 | "todomvc-app-css": "~2.3.0",
26 | "todomvc-common": "~1.0.5",
27 | "typescript": "~3.7.4",
28 | "underscore": "~1.9.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/typescript-example/src/app.ts:
--------------------------------------------------------------------------------
1 | import AppView from './views/app-view';
2 |
3 | // Kick things off by creating the `App`. Bind it to the existing '.todoapp'
4 | // element.
5 | const app = new AppView({ el: '.todoapp' });
6 |
--------------------------------------------------------------------------------
/typescript-example/src/collections/todos.ts:
--------------------------------------------------------------------------------
1 | import 'backbone.localstorage';
2 | import { Collection, LocalStorage } from 'backbone';
3 | import Todo from '../models/todo';
4 |
5 | // Todo Collection
6 | // ---------------
7 |
8 | // The collection of todos is backed by *localStorage* instead of a remote
9 | // server.
10 | export default class Todos extends Collection {
11 | constructor(models?: Todo[], options?: any) {
12 | super(models, options);
13 |
14 | this.model = Todo;
15 |
16 | // Todos are sorted by their original insertion order.
17 | this.comparator = todo => todo.get('order');
18 | }
19 |
20 | // Filter down the list of all todo items that are finished.
21 | completed() {
22 | return this.where({completed: true});
23 | }
24 |
25 | // Save all of the todo items under the `"todos"` namespace.
26 | localStorage() {
27 | return new LocalStorage('todos-backbone');
28 | }
29 |
30 | // Filter down the list to only todo items that are still not finished.
31 | remaining() {
32 | return this.where({completed: false});
33 | }
34 |
35 | // We keep the Todos in sequential order, despite being saved by unordered
36 | // GUID in the database. This generates the next order number for new items.
37 | nextOrder() {
38 | if (!this.length) {
39 | return 1;
40 | }
41 | return this.last().get('order') + 1;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/typescript-example/src/config.ts:
--------------------------------------------------------------------------------
1 | SystemJS.config({
2 | map: {
3 | backbone: 'node_modules/backbone/backbone.js',
4 | underscore: 'node_modules/underscore/underscore.js',
5 | jquery: 'node_modules/jquery/dist/jquery.js',
6 | 'backbone.localstorage':
7 | 'node_modules/backbone.localstorage/build/backbone.localStorage.js'
8 | },
9 | packages: {
10 | src: {
11 | defaultExtension: 'js'
12 | }
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/typescript-example/src/models/todo.ts:
--------------------------------------------------------------------------------
1 | import { Model } from 'backbone';
2 |
3 | // Todo Model
4 | // ----------
5 |
6 | // Our basic **Todo** model has `title`, `order`, and `completed` attributes.
7 | export default class Todo extends Model {
8 | // Default attributes for the todo
9 | // and ensure that each todo created has `title` and `completed` keys.
10 | defaults() {
11 | return {
12 | title: '',
13 | completed: false
14 | }
15 | }
16 |
17 | // Toggle the `completed` state of this todo item.
18 | toggle() {
19 | this.save({
20 | completed: !this.get('completed')
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/typescript-example/src/routers/router.ts:
--------------------------------------------------------------------------------
1 | import { Router, RouterOptions, history } from 'backbone';
2 | import Todos from '../collections/todos';
3 |
4 | // Todo Router
5 | // ----------
6 | export default class TodoRouter extends Router {
7 | filter: string;
8 | todos: Todos;
9 |
10 | static routes = {
11 | '*filter': 'setFilter'
12 | }
13 |
14 | constructor(options: TodoRouterOptions) {
15 | super({...options, routes: TodoRouter.routes });
16 | this.todos = options.todos;
17 | if (options.disableHistory !== false) {
18 | history.start();
19 | }
20 | }
21 |
22 | setFilter(param = '') {
23 | // Set the current filter to be used
24 | this.filter = param;
25 |
26 | // Trigger a collection filter event, causing hiding/unhiding
27 | // of Todo view items
28 | this.todos.trigger('filter');
29 | }
30 | }
31 |
32 | export interface TodoRouterOptions extends Partial {
33 | todos: Todos;
34 | disableHistory?: boolean;
35 | }
36 |
--------------------------------------------------------------------------------
/typescript-example/src/views/todo-view.ts:
--------------------------------------------------------------------------------
1 | import { View, ViewOptions } from 'backbone';
2 | import * as JQuery from 'jquery';
3 | import { template } from 'underscore';
4 | import Router from '../routers/router';
5 | import Todo from '../models/todo';
6 |
7 | // Todo Item View
8 | // --------------
9 |
10 | // The DOM element for a todo item
11 | export default class TodoView extends View {
12 | // Cache the template function for a single item.
13 | template: (...args: any[]) => string;
14 |
15 | $input: JQuery;
16 | router: Router;
17 |
18 | constructor(options: TodoViewOptions) {
19 | super({ tagName: 'li', ...>options });
20 |
21 | this.router = options.router;
22 | this.template = template($('#item-template').html());
23 |
24 | // The TodoView listens for changes to its model, re-rendering. Since there's
25 | // a one-to-one correspondence between a **Todo** and a **TodoView** in this
26 | // app, we set a direct reference on the model for convenience.
27 | this.listenTo(this.model, 'change', () => this.render());
28 | this.listenTo(this.model, 'destroy', () => this.remove());
29 | this.listenTo(this.model, 'visible', () => this.toggleVisible());
30 | }
31 |
32 | // The DOM events specific to an item.
33 | events() {
34 | return {
35 | 'click .toggle': 'toggleCompleted',
36 | 'dblclick label': 'edit',
37 | 'click .destroy': 'clear',
38 | 'keypress .edit': 'updateOnEnter',
39 | 'keydown .edit': 'revertOnEscape',
40 | 'blur .edit': 'close'
41 | }
42 | }
43 |
44 | // Re-render the titles of the todo item.
45 | render() {
46 | this.$el.html(this.template(this.model.toJSON()));
47 | this.$el.toggleClass('completed', this.model.get('completed'));
48 | this.toggleVisible();
49 | this.$input = > this.$('.edit');
50 | return this;
51 | }
52 |
53 | toggleVisible() {
54 | this.$el.toggleClass('hidden', this.isHidden());
55 | }
56 |
57 | isHidden() {
58 | const filter = this.router.filter;
59 | return this.model.get('completed') ?
60 | filter === 'active' :
61 | filter === 'completed';
62 | }
63 |
64 | // Toggle the `"completed"` state of the model.
65 | toggleCompleted() {
66 | this.model.toggle();
67 | }
68 |
69 | // Switch this view into `"editing"` mode, displaying the input field.
70 | edit() {
71 | var textLength = ( this.$input.val()).length;
72 | this.$el.addClass('editing');
73 | this.$input.focus();
74 | this.$input[0].setSelectionRange(textLength, textLength);
75 | }
76 |
77 | // Close the `"editing"` mode, saving changes to the todo.
78 | close() {
79 | const trimmedValue = (this.$input.val()).trim();
80 | this.$input.val(trimmedValue);
81 |
82 | // We don't want to handle blur events from an item that is no
83 | // longer being edited. Relying on the CSS class here has the
84 | // benefit of us not having to maintain state in the DOM and the
85 | // JavaScript logic.
86 | if (!this.$el.hasClass('editing')) {
87 | return;
88 | }
89 |
90 | if (trimmedValue) {
91 | this.model.save({ title: trimmedValue });
92 | } else {
93 | this.clear();
94 | }
95 |
96 | this.$el.removeClass('editing');
97 | }
98 |
99 | // If you hit `enter`, we're through editing the item.
100 | updateOnEnter(e) {
101 | if (e.which === 13) {
102 | this.close();
103 | }
104 | }
105 |
106 | // If you're pressing `escape` we revert your change by simply leaving
107 | // the `editing` state.
108 | revertOnEscape(e) {
109 | if (e.which === 27) {
110 | this.$el.removeClass('editing');
111 | // Also reset the hidden input back to the original value.
112 | this.$input.val(this.model.get('title'));
113 | }
114 | }
115 |
116 | // Remove the item, destroy the model from *localStorage* and delete its view.
117 | clear() {
118 | this.model.destroy();
119 | }
120 | }
121 |
122 | export interface TodoViewOptions extends ViewOptions {
123 | router: Router;
124 | }
125 |
--------------------------------------------------------------------------------
/typescript-example/tests/functional/Todo.ts:
--------------------------------------------------------------------------------
1 | const { registerSuite } = intern.getInterface('object');
2 | const { assert } = intern.getPlugin('chai');
3 |
4 | import keys from '@theintern/leadfoot/keys';
5 |
6 | registerSuite('Todo (functional)', {
7 | 'submit form'() {
8 | return this.remote
9 | .get('index.html')
10 | .findByCssSelector('.new-todo')
11 | .type('Task 1')
12 | .type(keys.RETURN)
13 | .type('Task 2')
14 | .type(keys.RETURN)
15 | .type('Task 3')
16 | .getSpecAttribute('value')
17 | .then(value => {
18 | assert.ok(
19 | value.indexOf('Task 3') > -1,
20 | 'Task 3 should remain in the new todo'
21 | );
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/typescript-example/tests/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "..",
5 |
6 | // Ensure the 'intern' global is deifned
7 | "types": [ "intern" ]
8 | },
9 | "include": [
10 | "./**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/typescript-example/tests/unit/models/todo.ts:
--------------------------------------------------------------------------------
1 | import Todo from 'src/models/todo';
2 |
3 | const { registerSuite } = intern.getInterface('object');
4 | const { assert } = intern.getPlugin('chai');
5 |
6 | let todo;
7 |
8 | registerSuite('todo model', {
9 | beforeEach() {
10 | todo = new Todo();
11 | todo.sync = () => Promise.resolve({});
12 | },
13 |
14 | tests: {
15 | defaults() {
16 | assert.isFalse(
17 | todo.get('completed'),
18 | 'A Todo model should default the completed property to false'
19 | );
20 | assert.strictEqual(
21 | todo.get('title'),
22 | '',
23 | 'A Todo model should default the title property to an empty string'
24 | );
25 | },
26 |
27 | toggle() {
28 | todo.toggle();
29 | assert.isTrue(
30 | todo.get('completed'),
31 | 'Completed property should switch to true after being toggled for the first time'
32 | );
33 |
34 | todo.toggle();
35 | assert.isFalse(
36 | todo.get('completed'),
37 | 'Completed property should switch back to false after being toggled again'
38 | );
39 | }
40 | }
41 | });
42 |
--------------------------------------------------------------------------------
/typescript-example/tests/unit/routers/router.ts:
--------------------------------------------------------------------------------
1 | import Router from 'src/routers/router';
2 |
3 | const { registerSuite } = intern.getInterface('object');
4 | const { assert } = intern.getPlugin('chai');
5 |
6 | registerSuite('router', {
7 | route() {
8 | let triggerType: string;
9 | const router = new Router({ todos: {
10 | trigger(type: string) {
11 | triggerType = type;
12 | }
13 | }, disableHistory: true });
14 | router.setFilter('foo');
15 | assert.equal(router.filter, 'foo');
16 | assert.equal(triggerType, 'filter');
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/typescript-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "inlineSourceMap": true,
4 | "lib": [
5 | "es2015",
6 | "dom",
7 | "dom.iterable"
8 | ],
9 | "module": "commonjs",
10 | "target": "es5",
11 |
12 | // Ensure the SystemJS global is defined
13 | "types": [ "systemjs" ]
14 | },
15 | "include": [
16 | "src/**/*.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/webpack-example/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | src/routers/router.js
3 | src/models/todo.js
4 |
--------------------------------------------------------------------------------
/webpack-example/README.md:
--------------------------------------------------------------------------------
1 | # webpack-example
2 |
3 | This example uses webpack to create a test bundle for a simple jQuery + Backbone app. The app is partially written in TypeScript to illustrate how to use TypeScript with webpack instrumentation.
4 |
5 | ## Setup
6 |
7 | 1. Install the JRE or JDK
8 | This demo runs with local Selenium, which Intern will automatically install.
9 |
10 | 2. Install intern command line interface (optional)
11 | ```
12 | npm install -g @theintern/cli
13 | ```
14 |
15 | 3. Install node modules and intern
16 | ```
17 | npm install
18 | ```
19 |
20 | 4. Build the test bundle
21 | ```
22 | npm run build
23 | ```
24 |
25 | ## Running tests
26 |
27 | * **Local browser tests**
28 |
29 | ```
30 | npm start
31 | ```
32 |
33 | Navigate to `http://localhost:9000/__intern/`
34 |
35 | * **Remote node / browser tests**
36 |
37 | ```
38 | npm test
39 | ```
40 |
41 | Remember to run `npm run build` again if any changes are made to the test or application code.
42 |
--------------------------------------------------------------------------------
/webpack-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Backbone.js • TodoMVC
6 |
7 |
8 |
9 |
10 |
22 |
27 |
35 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/webpack-example/intern.json:
--------------------------------------------------------------------------------
1 | {
2 | "environments": { "browserName": "chrome", "fixSessionCapabilities": "no-detect" },
3 | "browser": { "suites": "dist/tests.js" }
4 | }
5 |
--------------------------------------------------------------------------------
/webpack-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-example",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "An example testing a Webpacked app with Intern",
6 | "scripts": {
7 | "build": "webpack",
8 | "test": "intern",
9 | "start": "intern serveOnly"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@theintern/istanbul-loader": "~1.0.0-beta.2",
15 | "backbone": "~1.4.0",
16 | "backbone.localstorage": "~2.0.2",
17 | "intern": "~4.7.1",
18 | "istanbul-lib-source-maps": "~4.0.0",
19 | "jquery": "~3.5.0",
20 | "todomvc-app-css": "^2.3.0",
21 | "todomvc-common": "~1.0.5",
22 | "ts-loader": "~6.2.1",
23 | "ts-node": "~8.5.4",
24 | "typescript": "~3.7.4",
25 | "umd-compat-loader": "~2.1.2",
26 | "underscore": "~1.9.1",
27 | "webpack": "~4.41.4",
28 | "webpack-cli": "~3.3.10",
29 | "webpack-dev-server": "~3.10.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/webpack-example/src/app.js:
--------------------------------------------------------------------------------
1 | /*global $ */
2 | /*jshint unused:false */
3 | require('./models/todo');
4 | require('./collections/todos.js');
5 | require('./routers/router');
6 | require('./views/todo-view.js');
7 | require('./views/app-view.js');
8 |
9 | var app = window.app = window.app || {};
10 | window.ENTER_KEY = 13;
11 |
12 | $(function () {
13 | 'use strict';
14 |
15 | // kick things off by creating the `App`
16 | new app.AppView();
17 | });
18 |
--------------------------------------------------------------------------------
/webpack-example/src/collections/todos.js:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | require('backbone.localstorage');
3 | var app = window.app = window.app || {};
4 |
5 | (function () {
6 | 'use strict';
7 |
8 | // Todo Collection
9 | // ---------------
10 |
11 | // The collection of todos is backed by *localStorage* instead of a remote
12 | // server.
13 | var Todos = Backbone.Collection.extend({
14 | // Reference to this collection's model.
15 | model: app.Todo,
16 |
17 | // Save all of the todo items under this example's namespace.
18 | localStorage: new Backbone.LocalStorage('todos-backbone'),
19 |
20 | // Filter down the list of all todo items that are finished.
21 | completed: function () {
22 | return this.where({completed: true});
23 | },
24 |
25 | // Filter down the list to only todo items that are still not finished.
26 | remaining: function () {
27 | return this.where({completed: false});
28 | },
29 |
30 | // We keep the Todos in sequential order, despite being saved by unordered
31 | // GUID in the database. This generates the next order number for new items.
32 | nextOrder: function () {
33 | return this.length ? this.last().get('order') + 1 : 1;
34 | },
35 |
36 | // Todos are sorted by their original insertion order.
37 | comparator: 'order'
38 | });
39 |
40 | // Create our global collection of **Todos**.
41 | app.todos = new Todos();
42 | })();
43 |
--------------------------------------------------------------------------------
/webpack-example/src/globals.d.ts:
--------------------------------------------------------------------------------
1 | declare const Backbone: any;
2 |
--------------------------------------------------------------------------------
/webpack-example/src/models/todo.ts:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | var app = window.app = window.app || {};
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | // Todo Model
8 | // ----------
9 |
10 | // Our basic **Todo** model has `title`, `order`, and `completed` attributes.
11 | app.Todo = Backbone.Model.extend({
12 | // Default attributes for the todo
13 | // and ensure that each todo created has `title` and `completed` keys.
14 | defaults: {
15 | title: '',
16 | completed: false
17 | },
18 |
19 | // Toggle the `completed` state of this todo item.
20 | toggle() {
21 | this.save({
22 | completed: !this.get('completed')
23 | });
24 | }
25 | });
26 | })();
27 |
--------------------------------------------------------------------------------
/webpack-example/src/routers/router.ts:
--------------------------------------------------------------------------------
1 | /*global Backbone */
2 | var app = window.app = window.app || {};
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | // Todo Router
8 | // ----------
9 | var TodoRouter = Backbone.Router.extend({
10 | routes: {
11 | '*filter': 'setFilter'
12 | },
13 |
14 | setFilter: function (param) {
15 | // Set the current filter to be used
16 | app.TodoFilter = param || '';
17 |
18 | // Trigger a collection filter event, causing hiding/unhiding
19 | // of Todo view items
20 | app.todos.trigger('filter');
21 | }
22 | });
23 |
24 | app.TodoRouter = new TodoRouter();
25 | Backbone.history.start();
26 | })();
27 |
--------------------------------------------------------------------------------
/webpack-example/tests/all.js:
--------------------------------------------------------------------------------
1 | define([
2 | './models/todo'
3 | ], function () {});
--------------------------------------------------------------------------------
/webpack-example/tests/functional/Todo.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'intern!object',
3 | 'intern/chai!assert',
4 | 'require'
5 | ], function (registerSuite, assert, require) {
6 | var url = '../../index.html';
7 |
8 | registerSuite({
9 | name: 'Todo (functional)',
10 |
11 | 'submit form': function () {
12 | return this.remote
13 | .get(require.toUrl(url))
14 | .findById('new-todo')
15 | .click()
16 | .pressKeys('Task 1')
17 | .pressKeys('\n')
18 | .pressKeys('Task 2')
19 | .pressKeys('\n')
20 | .pressKeys('Task 3')
21 | .getProperty('value')
22 | .then(function (val) {
23 | assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
24 | });
25 | }
26 | });
27 | });
--------------------------------------------------------------------------------
/webpack-example/tests/intern.js:
--------------------------------------------------------------------------------
1 | require('./models/todo');
2 | require('./routers/router');
3 |
--------------------------------------------------------------------------------
/webpack-example/tests/models/todo.js:
--------------------------------------------------------------------------------
1 | /* global intern, jQuery */
2 | var registerSuite = intern.getPlugin('interface.object').registerSuite;
3 | var assert = intern.getPlugin('chai').assert;
4 |
5 | require('../../src/models/todo');
6 |
7 | var todo;
8 | var ajax;
9 |
10 | registerSuite('todo model', {
11 | before: function () {
12 | // Extend the Todo model only to add a urlRoot. This
13 | // property is required for a model to call save()
14 | // without throwing an error
15 | var Model = window.app.Todo.extend({
16 | urlRoot: 'mockUrlRoot'
17 | });
18 | todo = new Model();
19 |
20 | // Mock the jquery ajax method for now
21 | ajax = jQuery.ajax;
22 | jQuery.ajax = function () {};
23 | },
24 |
25 | after: function () {
26 | jQuery.ajax = ajax;
27 | intern.log('coverage:', window.__coverage__);
28 | },
29 |
30 | defaults: function () {
31 | assert.isFalse(todo.get('completed'), 'A Todo model should default the completed property to false');
32 | assert.strictEqual(todo.get('title'), '', 'A Todo model should default the title property to an empty string');
33 | },
34 |
35 | toggle: function () {
36 | todo.toggle();
37 | assert.isTrue(todo.get('completed'), '', 'Completed property should switch to true after being toggled for the first time');
38 | todo.toggle();
39 | assert.isFalse(todo.get('completed'), '', 'Completed property should switch back to false after being toggled again');
40 | }
41 | });
42 |
--------------------------------------------------------------------------------
/webpack-example/tests/routers/router.js:
--------------------------------------------------------------------------------
1 | /* global intern */
2 | var registerSuite = intern.getPlugin('interface.object').registerSuite;
3 | var assert = intern.getPlugin('chai').assert;
4 |
5 | var router;
6 | var triggerType;
7 |
8 | registerSuite('router', {
9 | before: function () {
10 | // Create a mock app with a todos collection
11 | window.app = {
12 | todos: {
13 | trigger: function (type) {
14 | triggerType = type;
15 | }
16 | }
17 | };
18 |
19 | // Require router here so it wil use the mock app
20 | require('../../src/routers/router');
21 | router = window.app.TodoRouter;
22 | },
23 |
24 | beforeEach: function () {
25 | triggerType = null;
26 | },
27 |
28 | route: function () {
29 | router.setFilter('foo');
30 | assert.equal(window.app.TodoFilter, 'foo');
31 | assert.equal(triggerType, 'filter');
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/webpack-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "inlineSourceMap": true,
4 | "module": "commonjs",
5 | "target": "es5"
6 | },
7 | "include": [
8 | "src/**/*.ts"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/webpack-example/webpack.config.ts:
--------------------------------------------------------------------------------
1 | import { ProvidePlugin } from 'webpack';
2 | import { resolve } from 'path';
3 |
4 | module.exports = {
5 | mode: 'development',
6 | entry: {
7 | app: './src/app.js',
8 | tests: './tests/intern.js'
9 | },
10 | devtool: 'inline-source-map',
11 | module: {
12 | rules: [
13 | { test: /\/js\/.*\.js$/, use: '@theintern/istanbul-loader' },
14 | { test: /\.ts$/, use: [ '@theintern/istanbul-loader', 'ts-loader' ] }
15 | ]
16 | },
17 | resolve: {
18 | extensions: ['.ts', '.js']
19 | },
20 | plugins: [
21 | new ProvidePlugin({
22 | jQuery: 'jquery',
23 | '_': 'underscore',
24 | Backbone: 'backbone'
25 | })
26 | ]
27 | };
28 |
--------------------------------------------------------------------------------