Repository: ionic-team/ionic-unit-testing-example
Branch: master
Commit: b4ee3f57d908
Files: 37
Total size: 31.9 KB
Directory structure:
gitextract_xporx_uk/
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── config.xml
├── e2e/
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.e2e.json
├── ionic.config.json
├── package.json
├── src/
│ ├── app/
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.html
│ │ ├── app.module.ts
│ │ ├── app.scss
│ │ └── main.ts
│ ├── index.html
│ ├── manifest.json
│ ├── pages/
│ │ ├── page1/
│ │ │ ├── page1.html
│ │ │ ├── page1.module.ts
│ │ │ ├── page1.scss
│ │ │ ├── page1.spec.ts
│ │ │ └── page1.ts
│ │ └── page2/
│ │ ├── page2.html
│ │ ├── page2.module.ts
│ │ ├── page2.scss
│ │ └── page2.ts
│ ├── service-worker.js
│ └── theme/
│ └── variables.scss
├── test-config/
│ ├── karma-test-shim.js
│ ├── karma.conf.js
│ ├── mocks-ionic.ts
│ ├── protractor.conf.js
│ └── webpack.test.js
├── tsconfig.json
├── tslint.json
└── typings/
└── cordova-typings.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitignore
================================================
# Specifies intentionally untracked files to ignore when using Git
# http://git-scm.com/docs/gitignore
*~
*.sw[mnpcod]
*.log
*.tmp
*.tmp.*
log.txt
*.sublime-project
*.sublime-workspace
.vscode/
npm-debug.log*
.idea/
.sass-cache/
.tmp/
.versions/
coverage/
dist/
node_modules/
tmp/
temp/
hooks/
platforms/
plugins/
plugins/android.json
plugins/ios.json
www/
$RECYCLE.BIN/
.DS_Store
Thumbs.db
UserInterfaceState.xcuserstate
\.sourcemaps/
================================================
FILE: LICENSE
================================================
Copyright 2015-present Drifty Co.
http://drifty.com/
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
> **2018-03-28:** We have a [pull request for updating to Karma 2](https://github.com/ionic-team/ionic-unit-testing-example/pull/87) awaiting review. If you can take the time to install this update and let us know if it works for you, that would be very helpful. Comments are welcome!
# Ionic Unit Testing Example
**This repository is an example project that gives guidance on setting up your Ionic application for unit testing and end-to-end (E2E) testing.** We have been told the folks at Ionic will be adding testing features to new projects in the future. But until then, feel free to borrow from this project as needed.
Special thanks to all of the contributors. With the exception of the Ionic team's updates to the framework, this project is stable. If you have a suggestion, feel free to update code and make a pull request. Find a problem or bug, feel free to file a detailed issue.
## How to Configure Your Ionic Application for Testing
Read [this tutorial](https://leifwells.github.io/2017/08/27/testing-in-ionic-configure-existing-projects-for-testing/) for instructions on how to apply the testing configuration in this project to your own project.
## Mocking Classes for Ionic
When a developer unit tests a component, the objective is to isolate that component as much as possible. In the case of an Ionic page, you may have Ionic components like `NavController`, `LoadingContoller`, or `Platform`. Adding these components means adding pieces of the Ionic framework to your test, thus not isolating the component. As part of this example, the file `test-config/mocks-ionic.ts` is provided for creating simple mocks for many of the classes in Ionic that you may need. You may use the classes from this file in test files inside the `TestBed.configureComponent()` method argument's `provider` array as seen in our [example unit test file](https://github.com/ionic-team/ionic-unit-testing-example/blob/master/src/app/app.component.spec.ts).
There are other mocking options that should be mentioned:
**[ionic-mocks](https://github.com/stonelasley/ionic-mocks)**
**[ionic-test-doubles](https://github.com/DomesticApp/ionic-test-doubles)**
Also worth mentioning is [ionic-native-mocks](https://github.com/chrisgriffith/ionic-native-mocks) which can be helpful when mocks for Ionic Native classes used in your project are needed.
Getting Started with this Project
-----------
To get started, clone this repo, and run `npm install` in the root directory.
```sh
git clone https://github.com/ionic-team/ionic-unit-testing-example.git
cd ionic-unit-testing-example
npm install
```
Then, you should run `ionic serve` to make sure the project loads.
### Unit Tests
To run the tests, run `npm run test`.
See the example test in `src/app/app.component.spec.ts` for an example of a component test.
### End-To-End Tests (Browser-Only)
To serve the app, run `ionic serve`.
To run the end-to-end tests, run (while the app is being served) `npm run e2e`.
See the example end-to-end test in `e2e/app.e2e-spec.ts`.
UPDATES:
--------
### **2018-02-25: Updated to Ionic 3.9.2**
Updated project to use the latest version Ionic and dependencies. Take a look at the [`package.json`](https://github.com/ionic-team/ionic-unit-testing-example/blob/master/package.json) for more details.
### **2017-09-05: Added Code Coverage Functionality**
Thanks to @lathonez, we now have functioning code coverage functionality. With this update, developers can run `npm run test-coverage` which will run the project's unit tests with Karma and produce documentation that gives guidance on how well the project's tests cover the code.

The documentation is created inside the `/coverage` folder (ignored by git).
## Looking for Version 2?
If you are using Ionic v2, please see our [ionic-v2-branch](https://github.com/ionic-team/ionic-unit-testing-example/tree/ionic-v2-branch).
## Credits
This repository is based on the awesome [unit testing example](https://github.com/roblouie/unit-testing-demo) from [@roblouie](https://github.com/roblouie/) :thumbsup:
Past Updates
==========
### **2017-08-26: Update to `ionic-angular` version 3.6.0**
We updated the project to `ionic-angular` version `3.6.0`. Developers wishing to use this should also update Ionic CLI for a better experience. With today's update, this project is now at version `0.0.5`.
## **Added: Fix for assets not being found by Karma**
Thanks to [@datencia](https://github.com/datencia) for providing an update to our Karma configuration that properly sets the Karma base path so that image assets can be found during testing. This fix should also work any JSON data files you might want to use for testing. He also provided an image and updated our sample unit test spec file that tests that the image is found.
#### **Added: Update code to match Ionic templates**
Thanks (again) to [@danielsogl](https://github.com/danielsogl/) for providing a code update to the project so that our code matches code created by using the `ionic start` command. Having this code properly match is important as it provides the best practice guidance for handling application start up.
#### *2017-05-28: Update to Version 3.3.0*
Thanks to [@danielsogl](https://github.com/danielsogl/) for getting us up-to-date with Ionic 3.3.0, Angular 4.1.2 and Ionic Native 3.10.2.
Thanks to [@johnstonbl01](https://github.com/johnstonbl01/) for adding a couple of mocks that prevent us from seeing some unnecessary warning messages.
#### *2017-05-12: Update to Version 3*
We've updated this repo to use Ionic v3.2.1 and Ionic CLI v3.0.0.
If you already have this repository downloaded on your system, after you sync with our Master branch make certain you delete your `node_modules` folder and then run `npm install`.
Also, since we've updated to Ionic CLI v3.0.0, you will need to update your version as well:
```
npm remove -g ionic
npm install -g ionic
```
If you run `ionic -v` it should return `3.0.0` (or better, depending on what has been released.
================================================
FILE: config.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.ionic.starter" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Ionic Unit and E2E Tests</name>
<description>An awesome Ionic/Cordova app.</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author>
<content src="index.html" />
<access origin="*" />
<allow-navigation href="http://ionic.local/*"/>
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
<preference name="webviewbounce" value="false" />
<preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />
<preference name="android-minSdkVersion" value="16" />
<preference name="BackupWebStorage" value="none" />
<!--
Change these to configure how the splashscreen displays and fades in/out.
More info here: https://github.com/apache/cordova-plugin-splashscreen
-->
<preference name="SplashMaintainAspectRatio" value="true" />
<preference name="FadeSplashScreenDuration" value="300" />
<preference name="SplashShowOnlyFirstTime" value="false" />
<feature name="StatusBar">
<param name="ios-package" onload="true" value="CDVStatusBar" />
</feature>
<plugin name="ionic-plugin-keyboard" spec="~2.2.1"/>
<plugin name="cordova-plugin-whitelist" spec="1.3.1"/>
<plugin name="cordova-plugin-statusbar" spec="2.2.1"/>
<plugin name="cordova-plugin-device" spec="1.1.4"/>
<plugin name="cordova-plugin-splashscreen" spec="~4.0.1"/>
</widget>
================================================
FILE: e2e/app.e2e-spec.ts
================================================
import { Page } from './app.po';
describe('App', () => {
let page: Page;
beforeEach(() => {
page = new Page();
});
describe('default screen', () => {
beforeEach(() => {
page.navigateTo('/');
});
it('should have a title saying Page One', () => {
page.getPageOneTitleText().then(title => {
expect(title).toEqual('Page One');
});
});
})
});
================================================
FILE: e2e/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class Page {
navigateTo(destination) {
return browser.get(destination);
}
getTitle() {
return browser.getTitle();
}
getPageOneTitleText() {
return element(by.tagName('page-page1')).element(by.tagName('ion-title')).element(by.css('.toolbar-title')).getText();
}
}
================================================
FILE: e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"typeRoots": [
"../node_modules/@types"
]
}
}
================================================
FILE: ionic.config.json
================================================
{
"name": "ionic-unit-testing-example",
"app_id": "",
"type": "ionic-angular",
"integrations": {}
}
================================================
FILE: package.json
================================================
{
"name": "ionic-unit-testing-example",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"private": true,
"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve",
"test": "karma start ./test-config/karma.conf.js",
"test-ci": "karma start ./test-config/karma.conf.js --single-run",
"test-coverage": "karma start ./test-config/karma.conf.js --coverage",
"e2e": "npm run e2e-update && npm run e2e-test",
"e2e-test": "protractor ./test-config/protractor.conf.js",
"e2e-update": "webdriver-manager update --standalone false --gecko false"
},
"dependencies": {
"@angular/common": "5.0.3",
"@angular/compiler": "5.0.3",
"@angular/compiler-cli": "5.0.3",
"@angular/core": "5.0.3",
"@angular/forms": "5.0.3",
"@angular/http": "5.0.3",
"@angular/platform-browser": "5.0.3",
"@angular/platform-browser-dynamic": "5.0.3",
"@ionic-native/core": "4.4.0",
"@ionic-native/splash-screen": "4.4.0",
"@ionic-native/status-bar": "4.4.0",
"@ionic/storage": "2.1.3",
"ionic-angular": "3.9.2",
"ionicons": "3.0.0",
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"
},
"devDependencies": {
"@angular/cli": "1.4.8",
"@ionic/app-scripts": "3.1.8",
"@types/jasmine": "^2.5.41",
"@types/node": "^8.0.45",
"angular2-template-loader": "^0.6.2",
"html-loader": "^0.5.1",
"istanbul-instrumenter-loader": "^3.0.0",
"jasmine": "^2.5.3",
"jasmine-spec-reporter": "^4.1.0",
"karma": "^1.5.0",
"karma-chrome-launcher": "^2.0.0",
"karma-coverage-istanbul-reporter": "^1.3.0",
"karma-jasmine": "^1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.3",
"null-loader": "^0.1.1",
"protractor": "^5.1.1",
"ts-loader": "^3.0.3",
"ts-node": "^3.0.2",
"typescript": "2.4.2"
},
"version": "0.0.7",
"description": "An Ionic project"
}
================================================
FILE: src/app/app.component.spec.ts
================================================
import { async, TestBed } from '@angular/core/testing';
import { IonicModule, Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { MyApp } from './app.component';
import {
PlatformMock,
StatusBarMock,
SplashScreenMock
} from '../../test-config/mocks-ionic';
describe('MyApp Component', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyApp],
imports: [
IonicModule.forRoot(MyApp)
],
providers: [
{ provide: StatusBar, useClass: StatusBarMock },
{ provide: SplashScreen, useClass: SplashScreenMock },
{ provide: Platform, useClass: PlatformMock }
]
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyApp);
component = fixture.componentInstance;
});
it('should be created', () => {
expect(component instanceof MyApp).toBe(true);
});
it('should have two pages', () => {
expect(component.pages.length).toBe(2);
});
});
================================================
FILE: src/app/app.component.ts
================================================
import { Component, ViewChild } from '@angular/core';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { Nav, Platform } from 'ionic-angular';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = 'Page1';
pages: Array<{ title: string, component: any }>;
constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) {
// used for an example of ngFor and navigation
this.pages = [
{ title: 'Page One', component: 'Page1' },
{ title: 'Page Two', component: 'Page2' }
];
}
ionViewDidLoad() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
openPage(page) {
// Reset the content nav to have just this page
// we wouldn't want the back button to show in this scenario
this.nav.setRoot(page.component);
}
}
================================================
FILE: src/app/app.html
================================================
<ion-menu [content]="content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
================================================
FILE: src/app/app.module.ts
================================================
import { ErrorHandler, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
@NgModule({
declarations: [
MyApp
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp
],
providers: [
StatusBar,
SplashScreen,
{ provide: ErrorHandler, useClass: IonicErrorHandler }
]
})
export class AppModule { }
================================================
FILE: src/app/app.scss
================================================
// http://ionicframework.com/docs/v2/theming/
// App Global Sass
// --------------------------------------------------
// Put style rules here that you want to apply globally. These
// styles are for the entire app and not just one component.
// Additionally, this file can be also used as an entry point
// to import other Sass files to be included in the output CSS.
//
// Shared Sass variables, which can be used to adjust Ionic's
// default Sass variables, belong in "theme/variables.scss".
//
// To declare rules for a specific mode, create a child rule
// for the .md, .ios, or .wp mode classes. The mode class is
// automatically applied to the <body> element in the app.
================================================
FILE: src/app/main.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
================================================
FILE: src/index.html
================================================
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4e8ef7">
<!-- cordova.js required for cordova apps -->
<script src="cordova.js"></script>
<!-- un-comment this code to enable service worker
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>-->
<link href="build/main.css" rel="stylesheet">
</head>
<body>
<!-- Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- The polyfills js is generated during the build process -->
<script src="build/polyfills.js"></script>
<!-- The vendor js is generated during the build process
It contains all of the dependencies in node_modules -->
<script src="build/vendor.js"></script>
<!-- The main bundle js is generated during the build process -->
<script src="build/main.js"></script>
</body>
</html>
================================================
FILE: src/manifest.json
================================================
{
"name": "Ionic",
"short_name": "Ionic",
"start_url": "index.html",
"display": "standalone",
"icons": [{
"src": "assets/imgs/logo.png",
"sizes": "512x512",
"type": "image/png"
}],
"background_color": "#4e8ef7",
"theme_color": "#4e8ef7"
}
================================================
FILE: src/pages/page1/page1.html
================================================
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Page One</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h3>Ionic Menu Starter</h3>
<img src="assets/icon/favicon.ico" alt="" />
<p>
If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will show you the way.
</p>
<button ion-button secondary menuToggle>Toggle Menu</button>
</ion-content>
================================================
FILE: src/pages/page1/page1.module.ts
================================================
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { Page1 } from './page1';
@NgModule({
declarations: [
Page1,
],
imports: [
IonicPageModule.forChild(Page1),
],
})
export class Page1Module {}
================================================
FILE: src/pages/page1/page1.scss
================================================
page-page1 {
}
================================================
FILE: src/pages/page1/page1.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { Page1 } from './page1';
import { IonicModule, Platform, NavController} from 'ionic-angular/index';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
describe('Page1', () => {
let de: DebugElement;
let comp: Page1;
let fixture: ComponentFixture<Page1>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [Page1],
imports: [
IonicModule.forRoot(Page1)
],
providers: [
NavController
]
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(Page1);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css('h3'));
});
it('should create component', () => expect(comp).toBeDefined());
it('should have expected <h3> text', () => {
fixture.detectChanges();
const h3 = de.nativeElement;
expect(h3.innerText).toMatch(/ionic/i,
'<h3> should say something about "Ionic"');
});
it('should show the favicon as <img>', () => {
fixture.detectChanges();
const img: HTMLImageElement = fixture.debugElement.query(By.css('img')).nativeElement;
expect(img.src).toContain('assets/icon/favicon.ico');
});
});
================================================
FILE: src/pages/page1/page1.ts
================================================
import { Component } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-page1',
templateUrl: 'page1.html'
})
export class Page1 {
constructor(public navCtrl: NavController) {
}
}
================================================
FILE: src/pages/page2/page2.html
================================================
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Page Two</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<button ion-item *ngFor="let item of items" (click)="itemTapped($event, item)">
<ion-icon [name]="item.icon" item-start></ion-icon>
{{item.title}}
<div class="item-note" item-end>
{{item.note}}
</div>
</button>
</ion-list>
<div *ngIf="selectedItem" padding>
You navigated here from <b>{{selectedItem.title}}</b>
</div>
</ion-content>
================================================
FILE: src/pages/page2/page2.module.ts
================================================
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { Page2 } from './page2';
@NgModule({
declarations: [
Page2,
],
imports: [
IonicPageModule.forChild(Page2),
],
})
export class Page2Module { }
================================================
FILE: src/pages/page2/page2.scss
================================================
page-page2 {
}
================================================
FILE: src/pages/page2/page2.ts
================================================
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-page2',
templateUrl: 'page2.html'
})
export class Page2 {
selectedItem: any;
icons: string[];
items: Array<{ title: string, note: string, icon: string }>;
constructor(public navCtrl: NavController, public navParams: NavParams) {
// If we navigated to this page, we will have an item available as a nav param
this.selectedItem = navParams.get('item');
// Let's populate this page with some filler content for funzies
this.icons = ['flask', 'wifi', 'beer', 'football', 'basketball', 'paper-plane',
'american-football', 'boat', 'bluetooth', 'build'];
this.items = [];
for (let i = 1; i < 11; i++) {
this.items.push({
title: 'Item ' + i,
note: 'This is item #' + i,
icon: this.icons[Math.floor(Math.random() * this.icons.length)]
});
}
}
itemTapped(event, item) {
// That's right, we're pushing to ourselves!
this.navCtrl.push('Page2', {
item: item
});
}
}
================================================
FILE: src/service-worker.js
================================================
/**
* Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for
* more info on how to use sw-toolbox to custom configure your service worker.
*/
'use strict';
importScripts('./build/sw-toolbox.js');
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
================================================
FILE: src/theme/variables.scss
================================================
// Ionic Variables and Theming. For more info, please see:
// http://ionicframework.com/docs/v2/theming/
$font-path: "../assets/fonts";
@import "ionic.globals";
// Shared Variables
// --------------------------------------------------
// To customize the look and feel of this app, you can override
// the Sass variables found in Ionic's source scss files.
// To view all the possible Ionic variables, see:
// http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/
// Named Color Variables
// --------------------------------------------------
// Named colors makes it easy to reuse colors on various components.
// It's highly recommended to change the default colors
// to match your app's branding. Ionic uses a Sass map of
// colors so you can add, rename and remove colors as needed.
// The "primary" color is the only required color in the map.
$colors: (
primary: #387ef5,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222
);
// App iOS Variables
// --------------------------------------------------
// iOS only Sass variables can go here
// App Material Design Variables
// --------------------------------------------------
// Material Design only Sass variables can go here
// App Windows Variables
// --------------------------------------------------
// Windows only Sass variables can go here
// App Theme
// --------------------------------------------------
// Ionic apps can have different themes applied, which can
// then be future customized. This import comes last
// so that the above variables are used and Ionic's
// default are overridden.
@import "ionic.theme.default";
// Ionicons
// --------------------------------------------------
// The premium icon font for Ionic. For more info, please see:
// http://ionicframework.com/docs/v2/ionicons/
@import "ionic.ionicons";
// Fonts
// --------------------------------------------------
@import "roboto";
@import "noto-sans";
================================================
FILE: test-config/karma-test-shim.js
================================================
Error.stackTraceLimit = Infinity;
require('core-js/es6');
require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
var appContext = require.context('../src', true, /\.spec\.ts/);
appContext.keys().forEach(appContext);
var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
================================================
FILE: test-config/karma.conf.js
================================================
var webpackConfig = require('./webpack.test.js');
module.exports = function(config) {
var _config = {
basePath: '../',
frameworks: ['jasmine'],
files: [
{
pattern: './test-config/karma-test-shim.js',
watched: true
},
{
pattern: './src/assets/**/*',
watched: false,
included: false,
served: true,
nocache: false
}
],
proxies: {
'/assets/': '/base/src/assets/'
},
preprocessors: {
'./test-config/karma-test-shim.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
stats: 'errors-only'
},
webpackServer: {
noInfo: true
},
browserConsoleLogOptions: {
level: 'log',
format: '%b %T: %m',
terminal: true
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
reporters: config.coverage ? ['kjhtml', 'dots', 'coverage-istanbul'] : ['kjhtml', 'dots'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
};
config.set(_config);
};
================================================
FILE: test-config/mocks-ionic.ts
================================================
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
export class PlatformMock {
public ready(): Promise<string> {
return new Promise((resolve) => {
resolve('READY');
});
}
public getQueryParam() {
return true;
}
public registerBackButtonAction(fn: Function, priority?: number): Function {
return (() => true);
}
public hasFocus(ele: HTMLElement): boolean {
return true;
}
public doc(): HTMLDocument {
return document;
}
public is(): boolean {
return true;
}
public getElementComputedStyle(container: any): any {
return {
paddingLeft: '10',
paddingTop: '10',
paddingRight: '10',
paddingBottom: '10',
};
}
public onResize(callback: any) {
return callback;
}
public registerListener(ele: any, eventName: string, callback: any): Function {
return (() => true);
}
public win(): Window {
return window;
}
public raf(callback: any): number {
return 1;
}
public timeout(callback: any, timer: number): any {
return setTimeout(callback, timer);
}
public cancelTimeout(id: any) {
// do nothing
}
public getActiveElement(): any {
return document['activeElement'];
}
}
export class StatusBarMock extends StatusBar {
styleDefault() {
return;
}
}
export class SplashScreenMock extends SplashScreen {
hide() {
return;
}
}
export class NavMock {
public pop(): any {
return new Promise(function(resolve: Function): void {
resolve();
});
}
public push(): any {
return new Promise(function(resolve: Function): void {
resolve();
});
}
public getActive(): any {
return {
'instance': {
'model': 'something',
},
};
}
public setRoot(): any {
return true;
}
public registerChildNav(nav: any): void {
return ;
}
}
export class DeepLinkerMock {
}
================================================
FILE: test-config/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'../e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:8100/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: test-config/webpack.test.js
================================================
var webpack = require('webpack');
var path = require('path');
module.exports = {
devtool: 'inline-source-map',
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [{
test: /\.ts$/,
loaders: [{
loader: 'ts-loader'
}, 'angular2-template-loader']
},
{
test: /.+\.ts$/,
exclude: /(index.ts|mocks.ts|\.spec\.ts$)/,
loader: 'istanbul-instrumenter-loader',
enforce: 'post',
query: {
esModules: true
}
},
{
test: /\.html$/,
loader: 'html-loader?attrs=false'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'null-loader'
}
]
},
plugins: [
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/(ionic-angular)|(angular(\\|\/)core(\\|\/)@angular)/,
root('./src'), // location of your src
{} // a map of your routes
)
]
};
function root(localPath) {
return path.resolve(__dirname, localPath);
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"dom",
"es2015"
],
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"target": "es5"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"src/**/*.spec.ts"
],
"compileOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}
================================================
FILE: tslint.json
================================================
{
"rules": {
"no-duplicate-variable": true,
"no-unused-variable": [
true
]
},
"rulesDirectory": [
"node_modules/tslint-eslint-rules/dist/rules"
]
}
================================================
FILE: typings/cordova-typings.d.ts
================================================
/// <reference path="../.vscode/typings/cordova-ionic/plugins/keyboard.d.ts"/>
/// <reference path="../.vscode/typings/jquery/jquery.d.ts"/>
gitextract_xporx_uk/
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── config.xml
├── e2e/
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.e2e.json
├── ionic.config.json
├── package.json
├── src/
│ ├── app/
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.html
│ │ ├── app.module.ts
│ │ ├── app.scss
│ │ └── main.ts
│ ├── index.html
│ ├── manifest.json
│ ├── pages/
│ │ ├── page1/
│ │ │ ├── page1.html
│ │ │ ├── page1.module.ts
│ │ │ ├── page1.scss
│ │ │ ├── page1.spec.ts
│ │ │ └── page1.ts
│ │ └── page2/
│ │ ├── page2.html
│ │ ├── page2.module.ts
│ │ ├── page2.scss
│ │ └── page2.ts
│ ├── service-worker.js
│ └── theme/
│ └── variables.scss
├── test-config/
│ ├── karma-test-shim.js
│ ├── karma.conf.js
│ ├── mocks-ionic.ts
│ ├── protractor.conf.js
│ └── webpack.test.js
├── tsconfig.json
├── tslint.json
└── typings/
└── cordova-typings.d.ts
SYMBOL INDEX (44 symbols across 10 files)
FILE: e2e/app.po.ts
class Page (line 3) | class Page {
method navigateTo (line 5) | navigateTo(destination) {
method getTitle (line 9) | getTitle() {
method getPageOneTitleText (line 13) | getPageOneTitleText() {
FILE: src/app/app.component.ts
class MyApp (line 10) | class MyApp {
method constructor (line 17) | constructor(public platform: Platform, public statusBar: StatusBar, pu...
method ionViewDidLoad (line 26) | ionViewDidLoad() {
method openPage (line 35) | openPage(page) {
FILE: src/app/app.module.ts
class AppModule (line 27) | class AppModule { }
FILE: src/pages/page1/page1.module.ts
class Page1Module (line 13) | class Page1Module {}
FILE: src/pages/page1/page1.ts
class Page1 (line 9) | class Page1 {
method constructor (line 11) | constructor(public navCtrl: NavController) {
FILE: src/pages/page2/page2.module.ts
class Page2Module (line 14) | class Page2Module { }
FILE: src/pages/page2/page2.ts
class Page2 (line 9) | class Page2 {
method constructor (line 14) | constructor(public navCtrl: NavController, public navParams: NavParams) {
method itemTapped (line 32) | itemTapped(event, item) {
FILE: test-config/mocks-ionic.ts
class PlatformMock (line 4) | class PlatformMock {
method ready (line 5) | public ready(): Promise<string> {
method getQueryParam (line 11) | public getQueryParam() {
method registerBackButtonAction (line 15) | public registerBackButtonAction(fn: Function, priority?: number): Func...
method hasFocus (line 19) | public hasFocus(ele: HTMLElement): boolean {
method doc (line 23) | public doc(): HTMLDocument {
method is (line 27) | public is(): boolean {
method getElementComputedStyle (line 31) | public getElementComputedStyle(container: any): any {
method onResize (line 40) | public onResize(callback: any) {
method registerListener (line 44) | public registerListener(ele: any, eventName: string, callback: any): F...
method win (line 48) | public win(): Window {
method raf (line 52) | public raf(callback: any): number {
method timeout (line 56) | public timeout(callback: any, timer: number): any {
method cancelTimeout (line 60) | public cancelTimeout(id: any) {
method getActiveElement (line 64) | public getActiveElement(): any {
class StatusBarMock (line 69) | class StatusBarMock extends StatusBar {
method styleDefault (line 70) | styleDefault() {
class SplashScreenMock (line 75) | class SplashScreenMock extends SplashScreen {
method hide (line 76) | hide() {
class NavMock (line 81) | class NavMock {
method pop (line 83) | public pop(): any {
method push (line 89) | public push(): any {
method getActive (line 95) | public getActive(): any {
method setRoot (line 103) | public setRoot(): any {
method registerChildNav (line 107) | public registerChildNav(nav: any): void {
class DeepLinkerMock (line 113) | class DeepLinkerMock {
FILE: test-config/protractor.conf.js
method onPrepare (line 22) | onPrepare() {
FILE: test-config/webpack.test.js
function root (line 48) | function root(localPath) {
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (36K chars).
[
{
"path": ".editorconfig",
"chars": 363,
"preview": "# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs\n# editor"
},
{
"path": ".gitignore",
"chars": 440,
"preview": "# Specifies intentionally untracked files to ignore when using Git\n# http://git-scm.com/docs/gitignore\n\n*~\n*.sw[mnpcod]\n"
},
{
"path": "LICENSE",
"chars": 1090,
"preview": "Copyright 2015-present Drifty Co.\nhttp://drifty.com/\n\nMIT License\n\nPermission is hereby granted, free of charge, to any "
},
{
"path": "README.md",
"chars": 6150,
"preview": "> **2018-03-28:** We have a [pull request for updating to Karma 2](https://github.com/ionic-team/ionic-unit-testing-exam"
},
{
"path": "config.xml",
"chars": 1986,
"preview": "<?xml version='1.0' encoding='utf-8'?>\n<widget id=\"io.ionic.starter\" version=\"0.0.1\" xmlns=\"http://www.w3.org/ns/widgets"
},
{
"path": "e2e/app.e2e-spec.ts",
"chars": 398,
"preview": "import { Page } from './app.po';\n\ndescribe('App', () => {\n let page: Page;\n\n beforeEach(() => {\n page = new Page();"
},
{
"path": "e2e/app.po.ts",
"chars": 349,
"preview": "import { browser, by, element } from 'protractor';\n\nexport class Page {\n\n navigateTo(destination) {\n return browser."
},
{
"path": "e2e/tsconfig.e2e.json",
"chars": 220,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/e2e\",\n \"baseUrl\": \"./\",\n \"modu"
},
{
"path": "ionic.config.json",
"chars": 108,
"preview": "{\n \"name\": \"ionic-unit-testing-example\",\n \"app_id\": \"\",\n \"type\": \"ionic-angular\",\n \"integrations\": {}\n}\n"
},
{
"path": "package.json",
"chars": 2104,
"preview": "{\n \"name\": \"ionic-unit-testing-example\",\n \"author\": \"Ionic Framework\",\n \"homepage\": \"https://ionicframework.com/\",\n "
},
{
"path": "src/app/app.component.spec.ts",
"chars": 1111,
"preview": "import { async, TestBed } from '@angular/core/testing';\nimport { IonicModule, Platform } from 'ionic-angular';\n\nimport {"
},
{
"path": "src/app/app.component.ts",
"chars": 1141,
"preview": "import { Component, ViewChild } from '@angular/core';\nimport { SplashScreen } from '@ionic-native/splash-screen';\nimport"
},
{
"path": "src/app/app.html",
"chars": 400,
"preview": "<ion-menu [content]=\"content\">\n <ion-header>\n <ion-toolbar>\n <ion-title>Menu</ion-title>\n </ion-toolbar>\n <"
},
{
"path": "src/app/app.module.ts",
"chars": 661,
"preview": "import { ErrorHandler, NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimpor"
},
{
"path": "src/app/app.scss",
"chars": 681,
"preview": "// http://ionicframework.com/docs/v2/theming/\n\n\n// App Global Sass\n// --------------------------------------------------"
},
{
"path": "src/app/main.ts",
"chars": 173,
"preview": "import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app.module';\n\n"
},
{
"path": "src/index.html",
"chars": 1445,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" dir=\"ltr\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Ionic App</title>\n <meta name=\"view"
},
{
"path": "src/manifest.json",
"chars": 266,
"preview": "{\n \"name\": \"Ionic\",\n \"short_name\": \"Ionic\",\n \"start_url\": \"index.html\",\n \"display\": \"standalone\",\n \"icons\": [{\n "
},
{
"path": "src/pages/page1/page1.html",
"chars": 478,
"preview": "<ion-header>\n <ion-navbar>\n <button ion-button menuToggle>\n <ion-icon name=\"menu\"></ion-icon>\n </button>\n "
},
{
"path": "src/pages/page1/page1.module.ts",
"chars": 257,
"preview": "import { NgModule } from '@angular/core';\nimport { IonicPageModule } from 'ionic-angular';\nimport { Page1 } from './page"
},
{
"path": "src/pages/page1/page1.scss",
"chars": 16,
"preview": "page-page1 {\n\n}\n"
},
{
"path": "src/pages/page1/page1.spec.ts",
"chars": 1407,
"preview": "import { async, ComponentFixture, TestBed } from '@angular/core/testing';\nimport { By } from '@angular/platfor"
},
{
"path": "src/pages/page1/page1.ts",
"chars": 262,
"preview": "import { Component } from '@angular/core';\nimport { IonicPage, NavController } from 'ionic-angular';\n\n@IonicPage()\n@Comp"
},
{
"path": "src/pages/page2/page2.html",
"chars": 595,
"preview": "<ion-header>\n <ion-navbar>\n <button ion-button menuToggle>\n <ion-icon name=\"menu\"></ion-icon>\n </button>\n "
},
{
"path": "src/pages/page2/page2.module.ts",
"chars": 259,
"preview": "import { NgModule } from '@angular/core';\nimport { IonicPageModule } from 'ionic-angular';\n\nimport { Page2 } from './pag"
},
{
"path": "src/pages/page2/page2.scss",
"chars": 16,
"preview": "page-page2 {\n\n}\n"
},
{
"path": "src/pages/page2/page2.ts",
"chars": 1118,
"preview": "import { Component } from '@angular/core';\nimport { IonicPage, NavController, NavParams } from 'ionic-angular';\n\n@IonicP"
},
{
"path": "src/service-worker.js",
"chars": 736,
"preview": "/**\n * Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for\n * more info on how to use sw-tool"
},
{
"path": "src/theme/variables.scss",
"chars": 1987,
"preview": "// Ionic Variables and Theming. For more info, please see:\n// http://ionicframework.com/docs/v2/theming/\n$font-path: \".."
},
{
"path": "test-config/karma-test-shim.js",
"chars": 689,
"preview": "Error.stackTraceLimit = Infinity;\n\nrequire('core-js/es6');\nrequire('core-js/es7/reflect');\n\nrequire('zone.js/dist/zone')"
},
{
"path": "test-config/karma.conf.js",
"chars": 1193,
"preview": "var webpackConfig = require('./webpack.test.js');\n\nmodule.exports = function(config) {\n var _config = {\n basePath: '"
},
{
"path": "test-config/mocks-ionic.ts",
"chars": 1959,
"preview": "import { StatusBar } from '@ionic-native/status-bar';\nimport { SplashScreen } from '@ionic-native/splash-screen';\n\nexpor"
},
{
"path": "test-config/protractor.conf.js",
"chars": 723,
"preview": "// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/blob/master/lib"
},
{
"path": "test-config/webpack.test.js",
"chars": 1095,
"preview": "var webpack = require('webpack');\nvar path = require('path');\n\nmodule.exports = {\n devtool: 'inline-source-map',\n\n res"
},
{
"path": "tsconfig.json",
"chars": 490,
"preview": "{\n \"compilerOptions\": {\n \"allowSyntheticDefaultImports\": true,\n \"declaration\": false,\n \"emitDecoratorMetadata\""
},
{
"path": "tslint.json",
"chars": 178,
"preview": "{\n \"rules\": {\n \"no-duplicate-variable\": true,\n \"no-unused-variable\": [\n true\n ]\n },\n \"rulesDirectory\": "
},
{
"path": "typings/cordova-typings.d.ts",
"chars": 141,
"preview": "\n/// <reference path=\"../.vscode/typings/cordova-ionic/plugins/keyboard.d.ts\"/>\n/// <reference path=\"../.vscode/typings/"
}
]
About this extraction
This page contains the full source code of the ionic-team/ionic-unit-testing-example GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (31.9 KB), approximately 9.2k tokens, and a symbol index with 44 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.