This commit is contained in:
Piyali Jana 2019-02-09 23:25:01 -08:00
Родитель 3d46614106
Коммит 2efadf35fd
70 изменённых файлов: 64556 добавлений и 0 удалений

14
vNext/AISKU/.npmignore Normal file
Просмотреть файл

@ -0,0 +1,14 @@
# NPM Ignore
# ignore everything
*
# ... but these files
!package.json
!tsconfig.json
!/README.md
!/LICENSE
!dist-esm/**
!dist/**
!browser/**
!src/**

355
vNext/AISKU/API.md Normal file
Просмотреть файл

@ -0,0 +1,355 @@
### trackPageView
```ts
applicationInsights.trackPageView(pageView: IPageViewTelemetry, customProperties?: { [key: string]: any })
```
The `IPageViewTelemetry` interface is below:
Parameter | Type | Description
---|---|---
`name?` | string | **Optional**<br>Name of the pageview. Defaults to the document `title`.
`uri?` | string | **Optional**<br>A relative or absolute URL that identifies the page or other item. Defaults to the window location.
`refUri?` | string | **Optional**<br>The URL of the previous page that sent the user to the current page.
`pageType?` | string | **Optional**<br>Page Type string. Describes how you classify this page, e.g. errorPage, formPage, etc.
`isLoggedIn?` | boolean | **Optional**<br>Whether or not the user is logged in
`pageTags?` | dictionary | **Optional**<br>Property bag to contain an extension to domain properties - extension to Part B
### startTrackPage
```ts
startTrackPage(name?: string)
```
Starts the timer for tracking a page load time. Use this instead of `trackPageView` if you want to control when the page view timer starts and stops, but don't want to calculate the duration yourself. This method doesn't send any telemetry. Call `stopTrackPage` to log the end of the page view and send the event.
Parameter | Type | Description
---|---|---
`name?` | string | **Optional**<br>The name used to identify the page in the portal. Defaults to the document title.
### stopTrackPage
```ts
stopTrackPage(name?: string, url?: string, customProperties?: { [name: string]: any; });
```
Stops the timer that was started by calling `startTrackPage` and sends the pageview load time telemetry with the specified properties and measurements. The duration of the page view will be the time between calling `startTrackPage` and `stopTrackPage`.
Parameter | Type | Description
---|---|---
`name?` | string | **Optional**<br>The name used to identify the page in the portal. Defaults to the document title.
`url?` | string | **Optional**<br>A relative or absolute URL that identifies the page or similar item. Defaults to the window location.
`customProperties?` | dictionary | **Optional**<br>Map of string to string: Additional data used to [filter pages](https://azure.microsoft.com/documentation/articles/app-insights-api-custom-events-metrics/#properties) in the portal. Defaults to empty.
### trackMetric
```ts
trackMetric(metric: IMetricTelemetry, customProperties?: {[name: string]: any})
```
Log a positive numeric value that is not associated with a specific event. Typically used to send regular reports of performance indicators.
To send a single measurement, use just the first two parameters. If you take measurements very frequently, you can reduce the telemetry bandwidth by aggregating multiple measurements and sending the resulting `average` and `sampleCount` at intervals.
`IMetricTelemetry` is described below
Parameter | Type | Description
---|---|---
`name` | string | **Required**<br>A string that identifies the metric. In the portal, you can select metrics for display by name.
`average` | number | **Required**<br>Either a single measurement, or the average of several measurements. Should be >=0 to be correctly displayed.
`sampleCount?` | number | **Optional**<br>Count of measurements represented by the average. Defaults to 1. Should be >=1.
`min?` | number | **Optional**<br>The smallest measurement in the sample. Defaults to the average. Should be >= 0.
`max?` | number | **Optional**<br>The largest measurement in the sample. Defaults to the average. Should be >= 0.
### trackException
```ts
trackException(exception: IExceptionTelemtry, customProperties?: {[key: string]: any})
```
Log an exception you have caught. Exceptions caught by the browser are also automatically logged.
`IExceptionTelemetry` is described below
Parameter | Type | Description
---|---|---
`error` | [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) | **Required**<br>Error object
`severityLevel?` | [SeverityLevel (number)](https://github.com/Microsoft/ApplicationInsights-JS/blob/master/JavaScript/JavaScriptSDK.Interfaces/Contracts/Generated/SeverityLevel.ts) | **Optional**<br>Severity of the message, ranging from verbose to critical
By default, uncaught browser exceptions are caught by the SDK and reported to the portal. To disable this behavior, insert the following line in the config section below your instrumentation key:
```ts
{
instrumentationKey: "your instrumentation key",
disableExceptionTracking: true
}
```
### trackTrace
```ts
trackTrace(trace: ITraceTelemetry, customProperties?: {[key: string]: any})
```
Log a diagnostic event such as entering or leaving a method.
The `ITraceTelemetry` interface is described below
Parameter | Type | Description
---|---|---
`message` | string | **Required**<br>Diagnostic data. Can be much longer than an event's name.
`severityLevel?` | [SeverityLevel (number)](https://github.com/Microsoft/ApplicationInsights-JS/blob/master/JavaScript/JavaScriptSDK.Interfaces/Contracts/Generated/SeverityLevel.ts) | **Optional**<br>Severity of the message, ranging from verbose to critical
In the portal, you can search on message content and [display individual trackTrace events](https://azure.microsoft.com/documentation/articles/app-insights-diagnostic-search/).
(Unlike `trackEvent`, you can't filter on the message content in the portal.)
### trackDependencyData
```ts
trackDependencyData(dependency: IDependencyTelemetry, customProperties?: {[key: string]: any}, systemProperties?: {[key: string]: any})
```
Log a dependency call (for instance: ajax)
The `IDependencyTelemetry` interface is described below
Parameter | Type | Description
---|---|---
`id` | string | **Required**<br>Unique id, this is used by the backend to correlate server requests.
`absoluteUrl` | string | **Required**<br>Absolute url used to make the dependency request
`success` | boolean | **Required**<br>Whether or not the request was successful or not (e.g., `responseCode` in the range 200-299)
`resultCode` | number | **Required**<br>Response code returned by the dependency request (e.g., `200` for a success)
`commandName?` | string| **Optional**<br>Command used to make the dependency request
`duration?` | number | **Optional**<br>Elapsed time of request & reply
`method?` | string | **Optional**<br>Represents request verb (GET, POST, etc.)
### flush
```ts
flush(async?: boolean = true)
```
Immediately send all queued telemetry. By default, it is sent async.
> *Note:* You don't have to use flush, as it is automatically called at an interval and when the user closes the window.
<a name="setAuthenticatedUserContext"></a>
### setAuthenticatedUserContext
```ts
setAuthenticatedUserContext(authenticatedUserId: string, accountId?: string, storeInCookie = false)
```
Set the authenticated user id and the account id. Use this when you have identified a specific signed-in user. Parameters must not contain spaces or ,;=|
The method will only set the `authenticatedUserId` and `accountId` for all events in the current page view. To set them for all events within the whole session, you should either call this method on every page view or set `storeInCookie = true`.
Parameter | Type |Description
---|---|--
`authenticatedUserId` | string | **Required**<br>An id that uniquely identifies a user of your app. No spaces, comma, semicolon, equals or vertical bar.
`accountId?` | string | **Optional**<br>An optional account id, if your app groups users into accounts. No spaces, comma, semicolon, equals or vertical bar.
In the portal, this will add to the count of authenticated users. Authenticated users provide a more reliable count of the number of real users than the count of anonymous users.
The authenticated user id will be available as part of the context of the telemetry sent to the portal, so that you can filter and search on it. It will also be saved as a cookie and sent to the server, where the server SDK (if installed) will attach it to server telemetry.
### clearAuthenticatedUserContext
```ts
clearAuthenticatedUserContext ()
```
Clears the authenticated user id and the account id from the user context, and clears the associated cookie.
### addTelemetryInitializer
```ts
public addTelemetryInitializer(telemetryInitializer: (item: ITelemetryItem) => boolean | void)
```
Adds a telemetry initializer to the collection. Telemetry initializers will be called one by one, in the order they were added,
before the telemetry item is pushed for sending.
If one of the telemetry initializers returns false or throws an error, then the telemetry item will not be sent.
```ts
interface ITelemetryItem {
/**
* Unique name of the telemetry item
*/
name: string;
/**
* Timestamp when item was sent
*/
timestamp?: Date;
/**
* Identifier of the resource that uniquely identifies which resource data is sent to
*/
instrumentationKey?: string;
/**
* System properties with well defined extensions, documentation coming soon
*/
ctx?: {[key: string]: any};
/**
* Part A custom extensions
*/
tags?: Tags[];
/**
* Telemetry type used for part B
*/
baseType?: string;
/**
* Based on schema for part B
*/
baseData?: { [key: string]: any };
/**
* Telemetry data used for Part C
*/
data?: {
[key: string]: any;
},
}
```
### Custom extension
A custom plugin can be loaded by the SDK through config.extensions. All plugins must implement ITelemetryPlugin interface. These provide the capability of inspecting and updating data as it leaves the system, but also provides additional functionality to for one time initialization of extension state and pass in custom configuration through SKU configuration etc.
```ts
interface ITelemetryPlugin {
/**
* Call back for telemetry processing before it is sent to next plugin for processing (needs to be invoked by caller)
*/
processTelemetry: (env: ITelemetryItem) => void;
/**
* Extension name
*/
identifier: string;
/**
* Set next extension for telemetry processing
*/
setNextPlugin: (next: ITelemetryPlugin) => void;
/**
* Priority of the extension
*
* 1 - 100: customer plugins
* 100 - 199: reserved for internal plugins.
* > 200: channel plugins (that implement IChannelControls to send data to an endpoint)
*/
priority: number;
}
```
## class TelemetryContext
### context.application
```ts
application: Context.Application
```
Details of the app you're monitoring.
```ts
context.application.ver: string
context.application.build : string
```
### context.device
```ts
device : Context.Device
```
The device the app is running on.
Property | Description
---|---
`device.type` | Type of device
`device.id` | unique ID
`device.oemName` |
`device.model` |
`device.network` | number - IANA interface type
`device.resolution` | screen res
`device.locale` | display language of the OS
`device.ip` |
`device.language` |
`device.os` | OS running on the device
`device.osversion` |
### context.user
```ts
user: Context.User
```
Data about the current user. Users are identified by cookie, so one person can look like
more than one user if they use different machines or browsers, or delete cookies.
Property | Description
---|---
`user.id` | Unique, cookie-based user id, automatically assigned.
`user.authenticatedId` | Id set by your app using [`setAuthenticatedUserContext`](#setAuthenticatedUserContext) when the user signs in.
`user.accountId` | Set by your app when the user signs in, if your app groups users into accounts.
`user.accountAcquisitionDate` |
`user.agent` |
`user.storeRegion` |
### context.session
```ts
session: Context.Session
```
The user session. A session represents a series of user actions. A session starts with a user action.
It ends at the last user activity when there is no more activity for sessionRenewalMs, or if it lasts longer than sessionExpirationMs.
Property | Description
---|---
`session.id` | Automatically assigned id
`session.isFirst` | Boolean. True if this is the first session for this user.
`session.acquisitionDate` | Number. The dateTime when this session was created.
`session.renewalDate` | Number. DateTime when telemetry was last sent with this session.
### context.location
```ts
location: Context.Location
```
Data from which the geographical location of the user's device can be guessed.
Property | Description
---|---
`location.ip` | IP address
### context.operation
```ts
operation: Context.Operation;
```
Represents the user request. Operation id is used to tie together related events in diagnostic search.
Property | Description
---|---
`id` | Unique id
`name` |
`parentId` |
`rootId` |
`syntheticSource` | String identifying the bot or test agent.

21
vNext/AISKU/LICENSE Normal file
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
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

229
vNext/AISKU/README.md Normal file
Просмотреть файл

@ -0,0 +1,229 @@
<properties
pageTitle="Application Insights SDK JavaScript API"
description="Reference doc"
services="application-insights"
documentationCenter=".net"
authors="markwolff"/>
<tags
ms.service="application-insights"
ms.workload="tbd"
ms.tgt_pltfrm="ibiza"
ms.devlang="na"
ms.topic="article"
ms.date="08/24/2015"/>
# Application Insights JavaScript SDK (Beta SDK)
[![Build Status](https://dev.azure.com/mseng/AppInsights/_apis/build/status/AppInsights%20-%20DevTools/1DS%20JavaScript%20SDK%20-%20SKU%20Web)](https://dev.azure.com/mseng/AppInsights/_build/latest?definitionId=7760)
[![Build Status](https://travis-ci.org/Microsoft/ApplicationInsights-JS.svg?branch=master)](https://travis-ci.org/Microsoft/ApplicationInsights-JS)
[![npm version](https://badge.fury.io/js/%40microsoft%2Fapplicationinsights-web.svg)](https://badge.fury.io/js/%40microsoft%2Fapplicationinsights-web)
[![minified size size](https://img.badgesize.io/https://az416426.vo.msecnd.net/beta/ai.1.min.js.svg?label=minified%20size)](https://img.badgesize.io/https://az416426.vo.msecnd.net/beta/ai.1.min.js.svg?label=minified%20size)
[![gzip size](https://img.badgesize.io/https://az416426.vo.msecnd.net/beta/ai.1.min.js.svg?compression=gzip&softmax=27000&max=30000)](https://img.badgesize.io/https://az416426.vo.msecnd.net/beta/ai.1.min.js.svg?compression=gzip&softmax=27000&max=30000)
## Getting Started
1. Create an Application Insights resource in Azure by following [these instructions](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-javascript?toc=/azure/azure-monitor/toc.json).
2. Grab the _Instrumentation Key_ (aka "ikey") from the resource you created in
step 1. Later, you'll add it to the `instrumentationKey` setting of the Application Insights JavaScript SDK.
3. Add Application Insights to your app. There are 2 ways to do this.
- Install via NPM. Then, [setup an instance Application Insights in your app](#setup-npm-only-ignore-if-using-snippet)
> *Note:* **Typings are included with this package**, so you do **not** need to install a separate typings package.
```sh
npm i --save @microsoft/applicationinsights-web
```
- [Pasting a script snippet at the beginning of every `<head>` tag in each of the pages of your app.](#snippet-setup-ignore-if-using-npm)
## Basic Usage
### Setup (NPM only, ignore if using Snippet)
```js
import { ApplicationInsights } from '@microsoft/applicationinsights-web'
```
```js
const appInsights = new ApplicationInsights({ config: {
instrumentationKey: 'YOUR_INSTRUMENTATION_KEY_GOES_HERE',
/* ...Other Configuration Options... */
}});
appInsights.loadAppInsights();
```
### Snippet Setup (Ignore if using NPM)
If your app does not use NPM, you can directly instrument your webpages with Application Insights by pasting this snippet at the top of each your pages. Preferably, it should be the first script in your `<head>` section so that it can monitor any potential issues with all of your dependencies.
```html
<script type="text/javascript">
var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(e){function n(e){t[e]=function(){var n=arguments;t.queue.push(function(){t[e].apply(t,n)})}}var t={config:e};t.initialize=!0;var i=document,a=window;setTimeout(function(){var n=i.createElement("script");n.src=e.url||"https://az416426.vo.msecnd.net/next/ai.1.min.js",i.getElementsByTagName("script")[0].parentNode.appendChild(n)});try{t.cookie=i.cookie}catch(e){}t.queue=[],t.version=2;
for(var r=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];r.length;)n("track"+r.pop());n("startTrackPage"),n("stopTrackPage");var o="Track"+r[0];
if(n("start"+o),n("stop"+o),!(!0===e.disableExceptionTracking||e.extensionConfig&&e.extensionConfig.ApplicationInsightsAnalytics&&!0===e.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){n("_"+(r="onerror"));var c=a[r];a[r]=function(e,n,i,a,o){var s=c&&c(e,n,i,a,o);return!0!==s&&t["_"+r]({message:e,url:n,lineNumber:i,columnNumber:a,error:o}),s},e.autoExceptionInstrumented=!0}return t}
({
instrumentationKey:"INSTRUMENTATION_KEY"
});if(window[aiName]=aisdk,aisdk.queue&&0===aisdk.queue.length){var pageViewItem={name:document.title?document.title:"",uri:document.URL?document.URL:""};aisdk.trackPageView(pageViewItem)}
</script>
```
### Sending Telemetry to the Azure Portal
If initialized using the snippet, your Application Insights instance is located by default at `window.appInsights`
```js
appInsights.trackEvent({name: 'some event'});
appInsights.trackPageView({name: 'some page'});
appInsights.trackPageViewPerformance({name : 'some page', url: 'some url'});
appInsights.trackException({error: new Error('some error')});
appInsights.trackTrace({message: 'some trace'});
appInsights.trackMetric({name: 'some metric', average: 42});
appInsights.trackDependencyData({absoluteUrl: 'some url', resultCode: 200, method: 'GET', id: 'some id'});
appInsights.startTrackPage("pageName");
appInsights.stopTrackPage("pageName", {customProp1: "some value"});
appInsights.startTrackEvent("event");
appInsights.stopTrackEvent("event", {customProp1: "some value"});
```
### Setting Up Autocollection
All autocollection is on by default. By using the full version of the JavaScript Application Insights SDK, we collect for you
- **Uncaught exceptions** in your app, including information on
- Stack trace
- Exception details and message accompanying the error
- Line & column number of error
- URL where error was raised
- **Network Dependency Requests** made by your app **XHR** and **Fetch** (fetch collection is disabled by default) requests, include information on
- Url of dependency source
- Command & Method used to request the dependency
- Duration of the request
- Result code and success status of the request
- ID (if any) of user making the request
- Correlation context (if any) where request is made
- **User information** (e.g. Location, network, IP)
- **Device information** (e.g. Browser, OS, version, language, resolution, model)
- **Session information**
### Telemetry Initializers
Telemetry initializers are used to modify the contents of collected telemetry before being sent from the user's browser. They can also be used to prevent certain telemetry from being sent, by returning `false`. Multiple telemetry initializers can be added to your Application Insights instance, and they are executed in order of adding them.
The input argument to `addTelemetryInitializer` is a callback that takes a [`ITelemetryItem`](./API.md#addTelemetryInitializer) as an argument and returns a `boolean` or `void`. If returning `false`, the telemetry item is not sent, else it proceeds to the next telemetry initializer, if any, or is sent to the telemetry collection endpoint.
An example of using telemetry initializers:
```ts
var telemetryInitializer = (envelope) => {
envelope.data.someField = 'This item passed through my telemetry initializer';
};
appInsights.addTelemetryInitializer(telemetryInitializer);
appInsights.trackTrace({message: 'This message will use a telemetry initializer'});
appInsights.addTelemetryInitializer(() => false); // Nothing is sent after this is executed
appInsights.trackTrace({message: 'this message will not be sent'}); // Not sent
```
## Configuration
Most configuration fields are named such that they can be defaulted to falsey. All fields are optional except for `instrumentationKey`.
| Name | Default | Description |
|------|---------|-------------|
| instrumentationKey | null | **Required**<br>Instrumentation key that you obtained from the Azure Portal. |
| accountId | null | An optional account id, if your app groups users into accounts. No spaces, commas, semicolons, equals, or vertical bars |
| sessionRenewalMs | 1800000 | A session is logged if the user is inactive for this amount of time in milliseconds. Default is 30 minutes |
| sessionExpirationMs | 86400000 | A session is logged if it has continued for this amount of time in milliseconds. Default is 24 hours |
| maxBatchSizeInBytes | 10000 | Max size of telemetry batch. If a batch exceeds this limit, it is immediately sent and a new batch is started |
| maxBatchInterval | 15000 | How long to batch telemetry for before sending (milliseconds) |
| disableExceptionTracking | false | If true, exceptions are no autocollected. Default is false. |
| disableTelemetry | false | If true, telemetry is not collected or sent. Default is false. |
| enableDebug | false | If true, **internal** debugging data is thrown as an exception **instead** of being logged, regardless of SDK logging settings. Default is false. <br>***Note:*** Enabling this setting will result in dropped telemetry whenever an internal error occurs. This can be useful for quickly identifying issues with your configuration or usage of the SDK. If you do not want to lose telemetry while debugging, consider using `consoleLoggingLevel` or `telemetryLoggingLevel` instead of `enableDebug`. |
| consoleLoggingLevel | 0 | Logs **internal** Application Insights errors to console. <br>0: off, <br>1: Critical errors only, <br>2: Everything (errors & warnings) |
| telemetryLoggingLevel | 1 | Sends **internal** Application Insights errors as telemetry. <br>0: off, <br>1: Critical errors only, <br>2: Everything (errors & warnings) |
| diagnosticLogInterval | 10000 | (internal) Polling interval (in ms) for internal logging queue |
| samplingPercentage | 100 | Percentage of events that will be sent. Default is 100, meaning all events are sent. Set this if you wish to preserve your datacap for large-scale applications. |
| autoTrackPageVisitTime | false | If true, on a pageview,the previous instrumented page's view time is tracked and sent as telemetry and a new timer is started for the current pageview. Default is false. |
| disableAjaxTracking | false | If true, Ajax calls are not autocollected. Default is false. |
| disableFetchTracking | true | If true, Fetch requests are not autocollected. Default is true |
| overridePageViewDuration | false | If true, default behavior of trackPageView is changed to record end of page view duration interval when trackPageView is called. If false and no custom duration is provided to trackPageView, the page view performance is calculated using the navigation timing API. Default is false. |
| maxAjaxCallsPerView | 500 | Default 500 - controls how many ajax calls will be monitored per page view. Set to -1 to monitor all (unlimited) ajax calls on the page. |
| disableDataLossAnalysis | true | If false, internal telemetry sender buffers will be checked at startup for items not yet sent. |
| disableCorrelationHeaders | false | If false, the SDK will add two headers ('Request-Id' and 'Request-Context') to all dependency requests to correlate them with corresponding requests on the server side. Default is false. |
| correlationHeaderExcludedDomains | | Disable correlation headers for specific domains |
| disableFlushOnBeforeUnload | false | Default false. If true, flush method will not be called when onBeforeUnload event triggers |
| enableSessionStorageBuffer | true | Default true. If true, the buffer with all unsent telemetry is stored in session storage. The buffer is restored on page load |
| isCookieUseDisabled | false | Default false. If true, the SDK will not store or read any data from cookies.|
| cookieDomain | null | Custom cookie domain. This is helpful if you want to share Application Insights cookies across subdomains. |
| isRetryDisabled | false | Default false. If false, retry on 206 (partial success), 408 (timeout), 429 (too many requests), 500 (internal server error), 503 (service unavailable), and 0 (offline, only if detected) |
| isStorageUseDisabled | false | If true, the SDK will not store or read any data from local and session storage. Default is false. |
| isBeaconApiDisabled | true | If false, the SDK will send all telemetry using the [Beacon API](https://www.w3.org/TR/beacon) |
| sdkExtension | null | Sets the sdk extension name. Only alphabetic characters are allowed. The extension name is added as a prefix to the 'ai.internal.sdkVersion' tag (e.g. 'ext_javascript:2.0.0'). Default is null. |
| isBrowserLinkTrackingEnabled | false | Default is false. If true, the SDK will track all [Browser Link](https://docs.microsoft.com/en-us/aspnet/core/client-side/using-browserlink) requests. |
| appId | null | AppId is used for the correlation between AJAX dependencies happening on the client-side with the server-side requets. When Beacon API is enabled, it cannot be used automatically, but can be set manually in the configuration. Default is null |
| enableCorsCorrelation | false | If true, the SDK will add two headers ('Request-Id' and 'Request-Context') to all CORS requests tocorrelate outgoing AJAX dependencies with corresponding requests on the server side. Default is false |
## Examples
For runnable examples, see [Application Insights Javascript SDK samples](https://github.com/Azure-Samples?utf8=%E2%9C%93&q=application+insights+sdk&type=&language=)
## Application Insights Web Basic
For a lightweight experience, you can instead install the basic version of Application Insights
```
npm i --save @microsoft/applicationinsights-web-basic
```
This version comes with the bare minimum amount of features and functionalities and relies on you to build it up as you see fit. For example, it performs no auto-collection (uncaught exceptions, ajax, etc). The APIs to send certain telemetry types, like `trackTrace`, `trackException`, etc, are not included in this version, so you will need to provide your own wrapper. The only api that is available is `track`.
## Upgrading from the Old Version of Application Insights
Breaking changes in the SDK V2 version:
- To allow for better API signatures, some of the apis such as trackPageView, trackException have been updated. Running in IE8 or lower versions of the browser is not supported.
- Telemetry envelope has some changes due to data schema updates.
If you are using the current application insights PRODUCTION SDK (1.0.20) and want to see if the new SDK works in runtime, please update URL depending on your current SDK loading scenario:
**a)** Download via CDN scenario:
Update code snippet that you currently use to point to the following URL:
```
"https://az416426.vo.msecnd.net/beta/ai.1.min.js"
```
**b)** NPM scenario:
Call downloadAndSetup to download full ApplicationInsights script from CDN and initialize it with instrumentation key.
```ts
appInsights.downloadAndSetup({
instrumentationKey: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
url: "https://az416426.vo.msecnd.net/beta/ai.1.min.js"
});
```
Test in internal environment to verify monitoring telemetry is working as expected. If all works, please update your api signatures appropriately to SDK V2 version and deploy in your production environments.
## Build & Test
1. Install all dependencies
```
npm install
```
2. Build and test
```
npm run build
npm run test
```
## Performance
At just 25 KB gzipped, and taking only ~15 ms to initialize, Application Insights adds a neglible amount of loadtime to your website. By using the snippet, minimal components of the library are quickly loaded, synchronously. In the meantime, the full script is downloaded in the background.
While the script is downloading from the CDN, all tracking of your page is queued. Once the downloaded script finishes asynchronously initializing, all events that were queued are tracked. As a result, you will not lose any telemetry during the entire life cycle of your page. This setup process provides your page with a seamless tracking system, invisible to your users.
> Summary:
> - **25 KB** gzipped
> - **15 ms** overall initialization time
> - **Zero** tracking missed during life cycle of page
## Browser Support
![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png) | ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png) | ![IE](https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png) | ![Opera](https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png) | ![Safari](https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png)
--- | --- | --- | --- | --- |
Latest ✔ | Latest ✔ | 9+ ✔ | Latest ✔ | Latest ✔ |
## Contributing
We strongly welcome and encourage contributions to this project. Please read the [contributor's guide][ContribGuide] located in the ApplicationInsights-Home repository. If making a large change we request that you open an [issue][GitHubIssue] first. We follow the [Git Flow][GitFlow] approach to branching.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
[ContribGuide]: https://github.com/Microsoft/ApplicationInsights-Home/blob/master/CONTRIBUTING.md
[GitFlow]: http://nvie.com/posts/a-successful-git-branching-model/
[GitHubIssue]: https://github.com/Microsoft/ApplicationInsights-JS/issues

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.13.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.14-beta.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.15.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.17.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.18.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
vNext/AISKU/dist-history/aisdk.0.0.19.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
vNext/AISKU/dist-history/snippet.min.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(n){var r={config:n,initialize:!0},i=document,e=window,t="script";setTimeout(function(){var e=i.createElement(t);e.src=n.url||"https://az416426.vo.msecnd.net/beta/ai.1.min.js",i.getElementsByTagName(t)[0].parentNode.appendChild(e)});try{r.cookie=i.cookie}catch(e){}function a(n){r[n]=function(){var e=arguments;r.queue.push(function(){r[n].apply(r,e)})}}r.queue=[];for(var c=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];c.length;)a("track"+c.pop());var o="TrackPage";if(a("start"+o),a("stop"+o),!(!0===n.disableExceptionTracking||n.extensionConfig&&n.extensionConfig.ApplicationInsightsAnalytics&&!0===n.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){a("_"+(c="onerror"));var s=e[c];e[c]=function(e,n,i,t,a){var o=s&&s(e,n,i,t,a);return!0!==o&&r["_"+c]({message:e,url:n,lineNumber:i,columnNumber:t,error:a}),o},n.autoExceptionInstrumented=!0}return r}({instrumentationKey:"INSTRUMENTATION_KEY"});if((window[aiName]=aisdk).queue&&0===aisdk.queue.length){var pageViewItem={name:document.title?document.title:"",uri:document.URL?document.URL:""};aisdk.trackPageView(pageViewItem)}

48
vNext/AISKU/package.json Normal file
Просмотреть файл

@ -0,0 +1,48 @@
{
"name": "@microsoft/applicationinsights-web",
"version": "1.0.0-beta.11",
"description": "Microsoft Application Insights Javascript SDK API 1.0 beta",
"main": "dist/applicationinsights-web.js",
"module": "dist-esm/applicationinsights-web.js",
"types": "dist-esm/applicationinsights-web.d.ts",
"sideEffects": false,
"scripts": {
"clean": "rm -rfv browser types dist-esm",
"build": "npm run build:esm && npm run build:browser && grunt snippetvnext",
"build:esm": "grunt aisku",
"build:browser": "rollup -c",
"test": "grunt aiskutests",
"nightwatch:chrome": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env chrome",
"nightwatch:firefox": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env firefox",
"nightwatch:edge": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env edge",
"nightwatch:ie8": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env ie8",
"nightwatch:ie9": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env ie9",
"nightwatch:ie10": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env ie10",
"nightwatch:ie11": "nightwatch -c Tests/nightwatch/nightwatch.json Tests/nightwatch/run_nightwatch.js --env ie11",
"nightwatch": "start node Tests/nightwatch/serve_nightwatch.js && npx concurrently \"npm run nightwatch:chrome\" \"npm run nightwatch:ie11\" \"npm run nightwatch:ie10\" && npm run nightwatch:done || npm run nightwatch:done",
"nightwatch:done": "curl http://localhost:8000/_done"
},
"devDependencies": {
"chromedriver": "^2.45.0",
"finalhandler": "^1.1.1",
"geckodriver": "^1.14.1",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-uglify": "^6.0.0",
"selenium-server-standalone-jar": "^3.141.5",
"serve-static": "^1.13.2",
"source-map-loader": "^0.2.3",
"typescript": "2.5.3"
},
"dependencies": {
"@microsoft/applicationinsights-analytics-js": "1.0.0-beta.15",
"@microsoft/applicationinsights-channel-js": "1.0.0-beta.14",
"@microsoft/applicationinsights-common": "1.0.0-beta.16",
"@microsoft/applicationinsights-core-js": "1.0.0-beta.5",
"@microsoft/applicationinsights-dependencies-js": "1.0.0-beta.16",
"@microsoft/applicationinsights-properties-js": "1.0.0-beta.11"
},
"peerDependencies": {
"tslib": "^1.9.3"
}
}

Просмотреть файл

@ -0,0 +1,91 @@
$storageAccountName = "daprodcdn";
$cacheControlValue = "max-age=604800, immutable";
$contentType = "text/javascript; charset=utf-8";
function Show-Menu
{
param (
[string]$Title = 'Select container name'
)
cls
Write-Host "================ $Title ================"
Write-Host "1: next"
Write-Host "2: beta"
Write-Host "3: scripts/b"
}
Show-Menu
$input = Read-Host "Select an option (1, 2, 3)"
switch ($input)
{
'1' {
$containerName = "next";
} '2' {
$containerName = "beta";
} '3' {
$containerName = "scripts";
}
}
# Add "b/" blob prefix for scripts container only
$blobPrefix = If ($containerName.equals("scripts")) {"b/"} Else {""}
# get js sdk directory
$jsSdkDir = Split-Path (Split-Path $MyInvocation.MyCommand.Path) -Parent;
Write-Host "Releasing from $jsSdkDir";
# find version number
$packageJsonPath = Join-Path $jsSdkDir -ChildPath "package.json"
if (-Not (Test-Path $packageJsonPath)) {
Write-Warning "'$packageJsonPath' file not found, please enter the top JSSDK directory.";
exit;
}
$packagesJson = (Get-Content $packageJsonPath -Raw) | ConvertFrom-Json
$version = $packagesJson.version;
# check if the minified dir exists
$jssdkMinDir = Join-Path $jssdkDir -ChildPath "browser\";
if (-Not (Test-Path $jssdkMinDir)) {
Write-Warning "'$jssdkMinDir' directory doesn't exist. Compile JSSDK first.";
exit;
}
Write-Host "Preparing js files...";
$releaseFromDir = Join-Path $jssdkMinDir -ChildPath $version;
if (Test-Path $releaseFromDir) {
Write-Warning "$releaseFromDir release directory already exists."
Write-Warning "Did you forget to change the version number?"
exit;
}
New-Item -ItemType directory -Path $releaseFromDir | Out-Null
Copy-Item ($jssdkMinDir + "ai.1.js") (Join-Path $releaseFromDir -ChildPath "ai.1.js")
Copy-Item ($jssdkMinDir + "ai.1.js") (Join-Path $releaseFromDir -ChildPath ("ai." + $version + ".js"))
Copy-Item ($jssdkMinDir + "ai.1.js.map") (Join-Path $releaseFromDir -ChildPath "ai.1.js.map")
Copy-Item ($jssdkMinDir + "ai.1.js.map") (Join-Path $releaseFromDir -ChildPath ("ai." + $version + ".js.map"))
Copy-Item ($jssdkMinDir + "ai.1.min.js") (Join-Path $releaseFromDir -ChildPath "ai.1.min.js")
Copy-Item ($jssdkMinDir + "ai.1.min.js") (Join-Path $releaseFromDir -ChildPath ("ai." + $version + ".min.js"))
Copy-Item ($jssdkMinDir + "ai.1.min.js.map") (Join-Path $releaseFromDir -ChildPath "ai.1.min.js.map")
Copy-Item ($jssdkMinDir + "ai.1.min.js.map") (Join-Path $releaseFromDir -ChildPath ("ai." + $version + ".min.js.map"))
Write-Host "Please review files in $releaseFromDir"
Write-Host "Files will be uploaded to Azure storage! Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
$storageAccountKey = Read-Host "Please enter '$storageAccountName' account key";
$azureContext = New-AzureStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey
# upload files to Azure Storage
$files = Get-ChildItem $releaseFromDir;
foreach($file in $files) {
Set-AzureStorageBlobContent -Container $containerName -File (Join-Path $releaseFromDir -ChildPath $file) -Blob ($blobPrefix + $file) -Context $azureContext -Properties @{CacheControl = $cacheControlValue; ContentType = $contentType}
}
Write-Host "Files uploaded successfully to Azure Storage."

Просмотреть файл

@ -0,0 +1,96 @@
var sdkInstance = "appInsightsSDK";
window[sdkInstance] = "appInsights";
var aiName = window[sdkInstance]; // provide non default instance name through key appInsightsSDK
var aisdk = window[aiName] || (function (aiConfig) {
var appInsights = {
config: aiConfig
};
appInsights.initialize = true; // initialize sdk on download
// Assigning these to local variables allows them to be minified to save space:
var localDocument = document, localWindow = window, scriptText = "script";
setTimeout(function () {
var scriptElement = localDocument.createElement(scriptText);
scriptElement.src = aiConfig.url || "https://az416426.vo.msecnd.net/next/ai.1.min.js";
localDocument.getElementsByTagName(scriptText)[0].parentNode.appendChild(scriptElement);
});
// capture initial cookie
try {
appInsights.cookie = localDocument.cookie;
} catch (e) { }
appInsights.queue = [];
appInsights.version = 2.0;
function createLazyMethod(name) {
// Define a temporary method that queues-up a the real method call
appInsights[name] = function () {
// Capture the original arguments passed to the method
var originalArguments = arguments;
// Queue-up a call to the real method
appInsights.queue.push(function () {
// Invoke the real method with the captured original arguments
appInsights[name].apply(appInsights, originalArguments);
});
};
}
var method = ["Event", "PageView", "Exception", "Trace", "DependencyData", "Metric", "PageViewPerformance"];
while (method.length) {
createLazyMethod("track" + method.pop());
}
var track = "Track";
var trackPage = track + "Page";
createLazyMethod("start" + trackPage);
createLazyMethod("stop" + trackPage);
var trackEvent = track + method[0];
createLazyMethod("start" + trackEvent);
createLazyMethod("stop" + trackEvent);
// Collect global errors
// Note: ApplicationInsightsAnalytics is the extension string identifier for
// AppAnalytics. It is defined in ApplicationInsights.ts:ApplicationInsights.identifer
if (!(aiConfig.disableExceptionTracking === true ||
(aiConfig.extensionConfig &&
aiConfig.extensionConfig.ApplicationInsightsAnalytics &&
aiConfig.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking === true))) {
method = "onerror";
createLazyMethod("_" + method);
var originalOnError = localWindow[method];
localWindow[method] = function(message, url, lineNumber, columnNumber, error) {
var handled = originalOnError && originalOnError(message, url, lineNumber, columnNumber, error);
if (handled !== true) {
appInsights["_" + method]({
message: message,
url: url,
lineNumber: lineNumber,
columnNumber: columnNumber,
error: error
});
}
return handled;
};
aiConfig.autoExceptionInstrumented = true;
}
return appInsights;
})({
instrumentationKey: "INSTRUMENTATION_KEY"
});
// global instance must be set in this order to mitigate issues in ie8 and lower
window[aiName] = aisdk;
// if somebody calls the snippet twice, don't report page view again
if (aisdk.queue && aisdk.queue.length === 0) {
var pageViewItem = {
name: document.title ? document.title : "",
uri: document.URL ? document.URL : ""
};
aisdk.trackPageView(pageViewItem);
}

1
vNext/AISKU/snippet/snippet.min.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(e){function n(e){t[e]=function(){var n=arguments;t.queue.push(function(){t[e].apply(t,n)})}}var t={config:e};t.initialize=!0;var i=document,a=window;setTimeout(function(){var n=i.createElement("script");n.src=e.url||"https://az416426.vo.msecnd.net/next/ai.1.min.js",i.getElementsByTagName("script")[0].parentNode.appendChild(n)});try{t.cookie=i.cookie}catch(e){}t.queue=[],t.version=2;for(var r=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];r.length;)n("track"+r.pop());n("startTrackPage"),n("stopTrackPage");var o="Track"+r[0];if(n("start"+o),n("stop"+o),!(!0===e.disableExceptionTracking||e.extensionConfig&&e.extensionConfig.ApplicationInsightsAnalytics&&!0===e.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){n("_"+(r="onerror"));var c=a[r];a[r]=function(e,n,i,a,o){var s=c&&c(e,n,i,a,o);return!0!==s&&t["_"+r]({message:e,url:n,lineNumber:i,columnNumber:a,error:o}),s},e.autoExceptionInstrumented=!0}return t}({instrumentationKey:"INSTRUMENTATION_KEY"});if(window[aiName]=aisdk,aisdk.queue&&0===aisdk.queue.length){var pageViewItem={name:document.title?document.title:"",uri:document.URL?document.URL:""};aisdk.trackPageView(pageViewItem)}

Просмотреть файл

@ -0,0 +1,26 @@
import { IAppInsightsDeprecated, AppInsightsDeprecated } from "./ApplicationInsightsDeprecated";
import { Initialization as ApplicationInsights, Snippet, IApplicationInsights } from "./Initialization";
export class ApplicationInsightsContainer {
public static getAppInsights(snippet: Snippet, version: number) : IApplicationInsights | IAppInsightsDeprecated {
let initialization = new ApplicationInsights(snippet);
let legacyMode = version !== 2.0 ? true : false;
// Two target scenarios:
// 1. Customer runs v1 snippet + runtime. If customer updates just cdn location to new SDK, it will run in compat mode so old apis work
// 2. Customer updates to new snippet (that uses cdn location to new SDK. This is same as a new customer onboarding
// and all api signatures are expected to map to new SDK. Note new snippet specifies version
if (version === 2.0) {
initialization.updateSnippetDefinitions(snippet);
initialization.loadAppInsights(legacyMode);
return initialization; // default behavior with new snippet
} else {
let legacy = new AppInsightsDeprecated(snippet, initialization); // target scenario old snippet + updated endpoint
legacy.updateSnippetDefinitions(snippet);
initialization.loadAppInsights(legacyMode);
return legacy;
}
}
}

Просмотреть файл

@ -0,0 +1,372 @@
import { IConfig, PageViewPerformance, SeverityLevel, Util,
IPageViewTelemetry, ITraceTelemetry, IMetricTelemetry,
IAutoExceptionTelemetry, IDependencyTelemetry, IExceptionTelemetry,
IEventTelemetry, IEnvelope, ProcessLegacy, HttpMethod } from "@microsoft/applicationinsights-common";
import { Snippet, IApplicationInsights } from "./Initialization";
import { ITelemetryItem, IDiagnosticLogger, IConfiguration } from "@microsoft/applicationinsights-core-js";
// ToDo: fix properties and measurements once updates are done to common
export class AppInsightsDeprecated implements IAppInsightsDeprecated {
public config: IConfig & IConfiguration;
public snippet: Snippet;
public context: ITelemetryContext;
public logger: IDiagnosticLogger;
queue: (() => void)[];
private appInsightsNew: IApplicationInsights;
private _hasLegacyInitializers = false;
private _queue = [];
/**
* The array of telemetry initializers to call before sending each telemetry item.
*/
public addTelemetryInitializers(callBack: (env: IEnvelope) => boolean | void) {
// Add initializer to current processing only if there is any old telemetry initializer
if (!this._hasLegacyInitializers) {
this.appInsightsNew.addTelemetryInitializer(item => {
this._processLegacyInitializers(item); // setup call back for each legacy processor
})
this._hasLegacyInitializers = true;
}
this._queue.push(callBack);
}
private _processLegacyInitializers(item: ITelemetryItem): ITelemetryItem {
// instead of mapping new to legacy and then back again and repeating in channel, attach callback for channel to call
item.tags[ProcessLegacy] = this._queue;
return item;
}
constructor(snippet: Snippet, appInsightsNew: IApplicationInsights) {
this.config = AppInsightsDeprecated.getDefaultConfig(snippet.config);
this.appInsightsNew = appInsightsNew;
this.context = { addTelemetryInitializer: this.addTelemetryInitializers.bind(this) }
}
startTrackPage(name?: string) {
this.appInsightsNew.startTrackPage(name);
}
stopTrackPage(name?: string, url?: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; }) {
this.appInsightsNew.stopTrackPage(name, url, properties); // update
}
trackPageView(name?: string, url?: string, properties?: {[key: string]: string }, measurements?: {[key: string]: number }, duration?: number) {
let telemetry: IPageViewTelemetry = {
name: name,
uri: url,
properties: properties,
measurements: measurements
};
// fix for props, measurements, duration
this.appInsightsNew.trackPageView(telemetry);
}
trackEvent(name: string, properties?: Object, measurements?: Object) {
this.appInsightsNew.trackEvent(<IEventTelemetry>{ name: name});
}
trackDependency(id: string, method: string, absoluteUrl: string, pathName: string, totalTime: number, success: boolean, resultCode: number) {
this.appInsightsNew.trackDependencyData(
<IDependencyTelemetry>{
id: id,
target: absoluteUrl,
type: pathName,
duration: totalTime,
properties: { HttpMethod: method },
success: success,
responseCode: resultCode
});
}
trackException(exception: Error, handledAt?: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; }, severityLevel?: any) {
this.appInsightsNew.trackException(<IExceptionTelemetry>{
error: exception
});
}
trackMetric(name: string, average: number, sampleCount?: number, min?: number, max?: number, properties?: { [name: string]: string; }) {
this.appInsightsNew.trackMetric(<IMetricTelemetry>{name: name, average: average, sampleCount: sampleCount, min: min, max: max});
}
trackTrace(message: string, properties?: { [name: string]: string; }, severityLevel?: any) {
this.appInsightsNew.trackTrace(<ITraceTelemetry>{ message: message, severityLevel: severityLevel });
}
flush(async?: boolean) {
this.appInsightsNew.flush(async);
}
setAuthenticatedUserContext(authenticatedUserId: string, accountId?: string, storeInCookie?: boolean) {
this.appInsightsNew.setAuthenticatedUserContext(authenticatedUserId, accountId, storeInCookie);
}
clearAuthenticatedUserContext() {
this.appInsightsNew.clearAuthenticatedUserContext();
}
_onerror(message: string, url: string, lineNumber: number, columnNumber: number, error: Error) {
this.appInsightsNew._onerror(<IAutoExceptionTelemetry>{ message: message, url: url, lineNumber: lineNumber, columnNumber: columnNumber, error: error });
}
startTrackEvent(name: string) {
this.appInsightsNew.startTrackEvent(name);
}
stopTrackEvent(name: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; }) {
this.appInsightsNew.stopTrackEvent(name, properties, measurements);
}
downloadAndSetup?(config: IConfig): void {
throw new Error("downloadAndSetup not implemented in web SKU");
}
public updateSnippetDefinitions(snippet: Snippet) {
// apply full appInsights to the global instance
// Note: This must be called before loadAppInsights is called
for (var field in this) {
if (typeof field === 'string') {
snippet[field as string] = this[field];
}
}
}
// note: these are split into methods to enable unit tests
public loadAppInsights() {
// initialize global instance of appInsights
//var appInsights = new Microsoft.ApplicationInsights.AppInsights(this.config);
// implement legacy version of trackPageView for 0.10<
if (this.config["iKey"]) {
var originalTrackPageView = this.trackPageView;
this.trackPageView = (pagePath?: string, properties?: Object, measurements?: Object) => {
originalTrackPageView.apply(this, [null, pagePath, properties, measurements]);
}
}
// implement legacy pageView interface if it is present in the snippet
var legacyPageView = "logPageView";
if (typeof this.snippet[legacyPageView] === "function") {
this[legacyPageView] = (pagePath?: string, properties?: {[key: string]: string }, measurements?: {[key: string]: number }) => {
this.trackPageView(null, pagePath, properties, measurements);
}
}
// implement legacy event interface if it is present in the snippet
var legacyEvent = "logEvent";
if (typeof this.snippet[legacyEvent] === "function") {
this[legacyEvent] = (name: string, props?: Object, measurements?: Object) => {
this.trackEvent(name, props, measurements);
}
}
return this;
}
private static getDefaultConfig(config?: any): any {
if (!config) {
config = <any>{};
}
// set default values
config.endpointUrl = config.endpointUrl || "https://dc.services.visualstudio.com/v2/track";
config.sessionRenewalMs = 30 * 60 * 1000;
config.sessionExpirationMs = 24 * 60 * 60 * 1000;
config.maxBatchSizeInBytes = config.maxBatchSizeInBytes > 0 ? config.maxBatchSizeInBytes : 102400; // 100kb
config.maxBatchInterval = !isNaN(config.maxBatchInterval) ? config.maxBatchInterval : 15000;
config.enableDebug = Util.stringToBoolOrDefault(config.enableDebug);
config.disableExceptionTracking = Util.stringToBoolOrDefault(config.disableExceptionTracking);
config.disableTelemetry = Util.stringToBoolOrDefault(config.disableTelemetry);
config.verboseLogging = Util.stringToBoolOrDefault(config.verboseLogging);
config.emitLineDelimitedJson = Util.stringToBoolOrDefault(config.emitLineDelimitedJson);
config.diagnosticLogInterval = config.diagnosticLogInterval || 10000;
config.autoTrackPageVisitTime = Util.stringToBoolOrDefault(config.autoTrackPageVisitTime);
if (isNaN(config.samplingPercentage) || config.samplingPercentage <= 0 || config.samplingPercentage >= 100) {
config.samplingPercentage = 100;
}
config.disableAjaxTracking = Util.stringToBoolOrDefault(config.disableAjaxTracking);
config.maxAjaxCallsPerView = !isNaN(config.maxAjaxCallsPerView) ? config.maxAjaxCallsPerView : 500;
config.isBeaconApiDisabled = Util.stringToBoolOrDefault(config.isBeaconApiDisabled, true);
config.disableCorrelationHeaders = Util.stringToBoolOrDefault(config.disableCorrelationHeaders);
config.correlationHeaderExcludedDomains = config.correlationHeaderExcludedDomains || [
"*.blob.core.windows.net",
"*.blob.core.chinacloudapi.cn",
"*.blob.core.cloudapi.de",
"*.blob.core.usgovcloudapi.net"];
config.disableFlushOnBeforeUnload = Util.stringToBoolOrDefault(config.disableFlushOnBeforeUnload);
config.enableSessionStorageBuffer = Util.stringToBoolOrDefault(config.enableSessionStorageBuffer, true);
config.isRetryDisabled = Util.stringToBoolOrDefault(config.isRetryDisabled);
config.isCookieUseDisabled = Util.stringToBoolOrDefault(config.isCookieUseDisabled);
config.isStorageUseDisabled = Util.stringToBoolOrDefault(config.isStorageUseDisabled);
config.isBrowserLinkTrackingEnabled = Util.stringToBoolOrDefault(config.isBrowserLinkTrackingEnabled);
config.enableCorsCorrelation = Util.stringToBoolOrDefault(config.enableCorsCorrelation);
return config;
}
}
export interface IAppInsightsDeprecated {
/*
* Config object used to initialize AppInsights
*/
config: IConfig;
context: ITelemetryContext;
/*
* Initialization queue. Contains functions to run when appInsights initializes
*/
queue: Array<() => void>;
/**
* Starts timing how long the user views a page or other item. Call this when the page opens.
* This method doesn't send any telemetry. Call `stopTrackPage` to log the page when it closes.
* @param name A string that idenfities this item, unique within this HTML document. Defaults to the document title.
*/
startTrackPage(name?: string);
/**
* Logs how long a page or other item was visible, after `startTrackPage`. Call this when the page closes.
* @param name The string you used as the name in startTrackPage. Defaults to the document title.
* @param url String - a relative or absolute URL that identifies the page or other item. Defaults to the window location.
* @param properties map[string, string] - additional data used to filter pages and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this page, displayed in Metrics Explorer on the portal. Defaults to empty.
* @deprecated API is deprecated; supported only if input configuration specifies deprecated=true
*/
stopTrackPage(name?: string, url?: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; });
/**
* Logs that a page or other item was viewed.
* @param name The string you used as the name in `startTrackPage`. Defaults to the document title.
* @param url String - a relative or absolute URL that identifies the page or other item. Defaults to the window location.
* @param properties map[string, string] - additional data used to filter pages and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this page, displayed in Metrics Explorer on the portal. Defaults to empty.
* @param duration number - the number of milliseconds it took to load the page. Defaults to undefined. If set to default value, page load time is calculated internally.
*/
trackPageView(name?: string, url?: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; }, duration?: number);
/**
* Start timing an extended event. Call `stopTrackEvent` to log the event when it ends.
* @param name A string that identifies this event uniquely within the document.
*/
startTrackEvent(name: string);
/**
* Log an extended event that you started timing with `startTrackEvent`.
* @param name The string you used to identify this event in `startTrackEvent`.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
stopTrackEvent(name: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; });
/**
* Log a user action or other occurrence.
* @param name A string to identify this event in the portal.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
trackEvent(name: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; });
/**
* Log a dependency call
* @param id unique id, this is used by the backend o correlate server requests. Use Util.newId() to generate a unique Id.
* @param method represents request verb (GET, POST, etc.)
* @param absoluteUrl absolute url used to make the dependency request
* @param pathName the path part of the absolute url
* @param totalTime total request time
* @param success indicates if the request was sessessful
* @param resultCode response code returned by the dependency request
*/
trackDependency(id: string, method: string, absoluteUrl: string, pathName: string, totalTime: number, success: boolean, resultCode: number);
/**
* Log an exception you have caught.
* @param exception An Error from a catch clause, or the string error message.
* @param handledAt Not used
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
* @param severityLevel SeverityLevel - severity level
*/
trackException(exception: Error, handledAt?: string, properties?: { [name: string]: string; }, measurements?: { [name: string]: number; }, severityLevel?: SeverityLevel);
/**
* Log a numeric value that is not associated with a specific event. Typically used to send regular reports of performance indicators.
* To send a single measurement, use just the first two parameters. If you take measurements very frequently, you can reduce the
* telemetry bandwidth by aggregating multiple measurements and sending the resulting average at intervals.
* @param name A string that identifies the metric.
* @param average Number representing either a single measurement, or the average of several measurements.
* @param sampleCount The number of measurements represented by the average. Defaults to 1.
* @param min The smallest measurement in the sample. Defaults to the average.
* @param max The largest measurement in the sample. Defaults to the average.
*/
trackMetric(name: string, average: number, sampleCount?: number, min?: number, max?: number, properties?: { [name: string]: string; });
/**
* Log a diagnostic message.
* @param message A message string
* @param properties map[string, string] - additional data used to filter traces in the portal. Defaults to empty.
* @param severityLevel SeverityLevel - severity level
*/
trackTrace(message: string, properties?: { [name: string]: string; }, severityLevel?: SeverityLevel);
/**
* Immediately send all queued telemetry.
* @param {boolean} async - If flush should be call asynchronously
*/
flush(async?: boolean);
/**
* Sets the autheticated user id and the account id in this session.
* User auth id and account id should be of type string. They should not contain commas, semi-colons, equal signs, spaces, or vertical-bars.
*
* @param authenticatedUserId {string} - The authenticated user id. A unique and persistent string that represents each authenticated user in the service.
* @param accountId {string} - An optional string to represent the account associated with the authenticated user.
*/
setAuthenticatedUserContext(authenticatedUserId: string, accountId?: string, storeInCookie?: boolean);
/**
* Clears the authenticated user id and the account id from the user context.
*/
clearAuthenticatedUserContext();
/*
* Downloads and initializes AppInsights. You can override default script download location by specifying url property of `config`.
*/
downloadAndSetup?(config: IConfig): void;
/**
* The custom error handler for Application Insights
* @param {string} message - The error message
* @param {string} url - The url where the error was raised
* @param {number} lineNumber - The line number where the error was raised
* @param {number} columnNumber - The column number for the line where the error was raised
* @param {Error} error - The Error object
*/
_onerror(message: string, url: string, lineNumber: number, columnNumber: number, error: Error);
}
export interface ITelemetryContext {
/**
* Adds a telemetry initializer to the collection. Telemetry initializers will be called one by one,
* in the order they were added, before the telemetry item is pushed for sending.
* If one of the telemetry initializers returns false or throws an error then the telemetry item will not be sent.
*/
addTelemetryInitializer(telemetryInitializer: (envelope: IEnvelope) => boolean | void);
}

36
vNext/AISKU/src/Init.ts Normal file
Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { Initialization as ApplicationInsights, Snippet } from "./Initialization";
import { ApplicationInsightsContainer } from "./ApplicationInsightsContainer";
export { Initialization as ApplicationInsights, Snippet } from "./Initialization";
"use strict";
//should be global function that should load as soon as SDK loads
try {
// E2E sku on load initializes core and pipeline using snippet as input for configuration
var aiName;
if (typeof window !== "undefined" && typeof JSON !== "undefined") {
// get snippet or initialize to an empty object
aiName = window["appInsightsSDK"] || "appInsights";
if (window[aiName] !== undefined) {
// this is the typical case for browser+snippet
var snippet: Snippet = window[aiName] || <any>{ version: 2.0 };
// overwrite snippet with full appInsights
// for 2.0 initialize only if required
if ((snippet.version === 2.0 && window[aiName].initialize) || snippet.version === undefined ) {
ApplicationInsightsContainer.getAppInsights(snippet, snippet.version);
}
}
}
} catch (e) {
// TODO: Find better place to warn to console when SDK initialization fails
if (console) {
console.warn('Failed to initialize AppInsights JS SDK for instance ' + aiName + e.message);
}
}

Просмотреть файл

@ -0,0 +1,358 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { IConfiguration, AppInsightsCore, IAppInsightsCore, LoggingSeverity, _InternalMessageId, ITelemetryItem } from "@microsoft/applicationinsights-core-js";
import { ApplicationInsights } from "@microsoft/applicationinsights-analytics-js";
import { Util, IConfig, IDependencyTelemetry, IPageViewPerformanceTelemetry,
IPageViewTelemetry, IExceptionTelemetry, IAutoExceptionTelemetry, ITraceTelemetry,
IMetricTelemetry, IEventTelemetry, IAppInsights, ConfigurationManager } from "@microsoft/applicationinsights-common";
import { Sender } from "@microsoft/applicationinsights-channel-js";
import { PropertiesPlugin, IPropertiesPlugin } from "@microsoft/applicationinsights-properties-js";
import { AjaxPlugin as DependenciesPlugin, IDependenciesPlugin } from '@microsoft/applicationinsights-dependencies-js';
"use strict";
/**
*
* @export
* @interface Snippet
*/
export interface Snippet {
config: IConfiguration & IConfig;
queue?: Array<() => void>;
version?: number;
}
export interface IApplicationInsights extends IAppInsights, IDependenciesPlugin, IPropertiesPlugin {
appInsights: ApplicationInsights;
flush: (async?: boolean) => void;
};
const propertiesPlugin = "AppInsightsPropertiesPlugin";
/**
* Application Insights API
* @class Initialization
* @implements {IApplicationInsights}
*/
export class Initialization implements IApplicationInsights {
public snippet: Snippet;
public config: IConfiguration & IConfig;
public appInsights: ApplicationInsights;
public core: IAppInsightsCore;
private dependencies: DependenciesPlugin;
private properties: PropertiesPlugin;
constructor(snippet: Snippet) {
// initialize the queue and config in case they are undefined
snippet.queue = snippet.queue || [];
snippet.version = snippet.version || 2.0; // Default to new version
var config: IConfiguration & IConfig = snippet.config || <any>{};
// ensure instrumentationKey is specified
if (config && !config.instrumentationKey) {
config = <any>snippet;
ApplicationInsights.Version = "2.0.2-beta";
}
this.appInsights = new ApplicationInsights();
this.properties = new PropertiesPlugin();
this.dependencies = new DependenciesPlugin();
this.core = new AppInsightsCore();
this.snippet = snippet;
this.config = config;
this.getSKUDefaults();
}
// Analytics Plugin
/**
* Log a user action or other occurrence.
* @param {IEventTelemetry} event
* @param {{ [key:string]: any }} [customProperties]
* @memberof Initialization
*/
public trackEvent(event: IEventTelemetry, customProperties?: { [key:string]: any }) {
this.appInsights.trackEvent(event, customProperties);
}
/**
* Logs that a page, or similar container was displayed to the user.
* @param {IPageViewTelemetry} pageView
* @memberof Initialization
*/
public trackPageView(pageView: IPageViewTelemetry) {
this.appInsights.trackPageView(pageView);
}
/**
* Log a bag of performance information via the customProperties field.
* @param {IPageViewPerformanceTelemetry} pageViewPerformance
* @memberof Initialization
*/
public trackPageViewPerformance(pageViewPerformance: IPageViewPerformanceTelemetry): void {
this.appInsights.trackPageViewPerformance(pageViewPerformance);
}
/**
* Log an exception that you have caught.
* @param {IExceptionTelemetry} exception
* @memberof Initialization
*/
public trackException(exception: IExceptionTelemetry): void {
this.appInsights.trackException(exception);
}
/**
* Manually send uncaught exception telemetry. This method is automatically triggered
* on a window.onerror event.
* @param {IAutoExceptionTelemetry} exception
* @memberof Initialization
*/
public _onerror(exception: IAutoExceptionTelemetry): void {
this.appInsights._onerror(exception);
}
/**
* Log a diagnostic scenario such entering or leaving a function.
* @param {ITraceTelemetry} trace
* @param {{ [key: string]: any; }} [customProperties]
* @memberof Initialization
*/
public trackTrace(trace: ITraceTelemetry, customProperties?: { [key: string]: any; }): void {
this.appInsights.trackTrace(trace, customProperties);
}
/**
* Log a numeric value that is not associated with a specific event. Typically used
* to send regular reports of performance indicators.
*
* To send a single measurement, just use the `name` and `average` fields
* of {@link IMetricTelemetry}.
*
* If you take measurements frequently, you can reduce the telemetry bandwidth by
* aggregating multiple measurements and sending the resulting average and modifying
* the `sampleCount` field of {@link IMetricTelemetry}.
* @param {IMetricTelemetry} metric input object argument. Only `name` and `average` are mandatory.
* @param {{ [key: string]: any; }} [customProperties]
* @memberof Initialization
*/
public trackMetric(metric: IMetricTelemetry, customProperties?: { [key: string]: any; }): void {
this.appInsights.trackMetric(metric, customProperties);
}
/**
* Starts the timer for tracking a page load time. Use this instead of `trackPageView` if you want to control when the page view timer starts and stops,
* but don't want to calculate the duration yourself. This method doesn't send any telemetry. Call `stopTrackPage` to log the end of the page view
* and send the event.
* @param name A string that idenfities this item, unique within this HTML document. Defaults to the document title.
*/
public startTrackPage(name?: string): void {
this.appInsights.startTrackPage(name);
}
/**
* Stops the timer that was started by calling `startTrackPage` and sends the pageview load time telemetry with the specified properties and measurements.
* The duration of the page view will be the time between calling `startTrackPage` and `stopTrackPage`.
* @param name The string you used as the name in startTrackPage. Defaults to the document title.
* @param url String - a relative or absolute URL that identifies the page or other item. Defaults to the window location.
* @param properties map[string, string] - additional data used to filter pages and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this page, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
public stopTrackPage(name?: string, url?: string, customProperties?: { [key: string]: any; }) {
this.appInsights.stopTrackPage(name, url, customProperties);
}
public startTrackEvent(name?: string): void {
this.appInsights.startTrackEvent(name);
}
/**
* Log an extended event that you started timing with `startTrackEvent`.
* @param name The string you used to identify this event in `startTrackEvent`.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
public stopTrackEvent(name: string, properties?: { [key: string]: string; }, measurements?: { [key: string]: number; }) {
this.appInsights.stopTrackEvent(name, properties, measurements); // Todo: Fix to pass measurements once type is updated
}
public addTelemetryInitializer(telemetryInitializer: (item: ITelemetryItem) => boolean | void) {
return this.appInsights.addTelemetryInitializer(telemetryInitializer);
}
// Properties Plugin
/**
* Set the authenticated user id and the account id. Used for identifying a specific signed-in user. Parameters must not contain whitespace or ,;=|
*
* The method will only set the `authenicatedUserId` and `accountId` in the curent page view. To set them for the whole sesion, you should set `storeInCookie = true`
* @param {string} authenticatedUserId
* @param {string} [accountId]
* @param {boolean} [storeInCookie=false]
* @memberof Initialization
*/
public setAuthenticatedUserContext(authenticatedUserId: string, accountId?: string, storeInCookie = false): void {
this.properties.user.setAuthenticatedUserContext(authenticatedUserId, accountId, storeInCookie);
}
/**
* Clears the authenticated user id and account id. The associated cookie is cleared, if present.
* @memberof Initialization
*/
public clearAuthenticatedUserContext(): void {
this.properties.user.clearAuthenticatedUserContext();
}
// Dependencies Plugin
/**
* Log a dependency call (e.g. ajax)
* @param {IDependencyTelemetry} dependency
* @memberof Initialization
*/
public trackDependencyData(dependency: IDependencyTelemetry): void {
this.dependencies.trackDependencyData(dependency);
}
// Misc
/**
* Manually trigger an immediate send of all telemetry still in the buffer.
* @param {boolean} [async=true]
* @memberof Initialization
*/
public flush(async: boolean = true) {
this.core.getTransmissionControls().forEach(channels => {
channels.forEach(channel => {
channel.flush(async);
})
})
}
/**
* Initialize this instance of ApplicationInsights
* @returns {IApplicationInsights}
* @memberof Initialization
*/
public loadAppInsights(legacyMode: boolean = false): IApplicationInsights {
// dont allow additional channels/other extensions for legacy mode; legacy mode is only to allow users to switch with no code changes!
if (legacyMode && this.config.extensions && this.config.extensions.length > 0) {
throw new Error("Extensions not allowed in legacy mode");
}
let extensions = [];
let appInsightsChannel: Sender = new Sender();
extensions.push(appInsightsChannel);
extensions.push(this.properties);
extensions.push(this.dependencies);
extensions.push(this.appInsights);
// initialize core
this.core.initialize(this.config, extensions);
// Empty queue of all api calls logged prior to sdk download
this.emptyQueue();
this.pollInternalLogs();
this.addHousekeepingBeforeUnload(this);
return this;
}
/**
* Overwrite the lazy loaded fields of global window snippet to contain the
* actual initialized API methods
* @param {Snippet} snippet
* @memberof Initialization
*/
public updateSnippetDefinitions(snippet: Snippet) {
// apply full appInsights to the global instance
// Note: This must be called before loadAppInsights is called
for (var field in this) {
if (typeof field === 'string') {
snippet[field as string] = this[field];
}
}
}
/**
* Call any functions that were queued before the main script was loaded
* @memberof Initialization
*/
public emptyQueue() {
// call functions that were queued before the main script was loaded
try {
if (Util.isArray(this.snippet.queue)) {
// note: do not check length in the for-loop conditional in case something goes wrong and the stub methods are not overridden.
var length = this.snippet.queue.length;
for (var i = 0; i < length; i++) {
var call = this.snippet.queue[i];
call();
}
this.snippet.queue = undefined;
delete this.snippet.queue;
}
} catch (exception) {
var properties: any = {};
if (exception && typeof exception.toString === "function") {
properties.exception = exception.toString();
}
// need from core
// Microsoft.ApplicationInsights._InternalLogging.throwInternal(
// LoggingSeverity.WARNING,
// _InternalMessageId.FailedToSendQueuedTelemetry,
// "Failed to send queued telemetry",
// properties);
}
}
public pollInternalLogs(): void {
this.core.pollInternalLogs();
}
public addHousekeepingBeforeUnload(appInsightsInstance: IApplicationInsights): void {
// Add callback to push events when the user navigates away
if (!appInsightsInstance.appInsights.config.disableFlushOnBeforeUnload && ('onbeforeunload' in window)) {
var performHousekeeping = function () {
// Adds the ability to flush all data before the page unloads.
// Note: This approach tries to push an async request with all the pending events onbeforeunload.
// Firefox does not respect this.Other browsers DO push out the call with < 100% hit rate.
// Telemetry here will help us analyze how effective this approach is.
// Another approach would be to make this call sync with a acceptable timeout to reduce the
// impact on user experience.
//appInsightsInstance.context._sender.triggerSend();
appInsightsInstance.flush(false);
// Back up the current session to local storage
// This lets us close expired sessions after the cookies themselves expire
// Todo: move this against interface behavior
if (appInsightsInstance.appInsights.core['_extensions'][propertiesPlugin] &&
appInsightsInstance.appInsights.core['_extensions'][propertiesPlugin]._sessionManager) {
appInsightsInstance.appInsights.core['_extensions'][propertiesPlugin]._sessionManager.backup();
}
};
if (!Util.addEventHandler('beforeunload', performHousekeeping)) {
appInsightsInstance.appInsights.core.logger.throwInternal(
LoggingSeverity.CRITICAL,
_InternalMessageId.FailedToAddHandlerForOnBeforeUnload,
'Could not add handler for beforeunload');
}
}
}
private getSKUDefaults() {
this.config.diagnosticLogInterval =
this.config.diagnosticLogInterval && this.config.diagnosticLogInterval > 0 ? this.config.diagnosticLogInterval : 10000;
}
}

Просмотреть файл

@ -0,0 +1,53 @@
export {
IApplicationInsights,
Snippet,
Initialization as ApplicationInsights
} from "./Initialization";
export { ApplicationInsightsContainer } from "./ApplicationInsightsContainer";
// Re-exports
export {
IConfiguration,
AppInsightsCore,
IAppInsightsCore,
LoggingSeverity,
_InternalMessageId,
ITelemetryItem,
ITelemetryPlugin
} from "@microsoft/applicationinsights-core-js";
export {
Util,
IConfig,
IDependencyTelemetry,
IPageViewPerformanceTelemetry,
IPageViewTelemetry,
IExceptionTelemetry,
IAutoExceptionTelemetry,
ITraceTelemetry,
IMetricTelemetry,
IEventTelemetry,
IAppInsights,
SeverityLevel,
Event,
Exception,
Metric,
PageView,
PageViewPerformance,
RemoteDependencyData,
Trace
} from "@microsoft/applicationinsights-common";
export { Sender } from "@microsoft/applicationinsights-channel-js";
export {
ApplicationInsights as ApplicationAnalytics
} from "@microsoft/applicationinsights-analytics-js";
export {
PropertiesPlugin,
IPropertiesPlugin,
Operation
} from "@microsoft/applicationinsights-properties-js";
export {
AjaxPlugin as DependenciesPlugin,
IDependenciesPlugin
} from "@microsoft/applicationinsights-dependencies-js";

Просмотреть файл

@ -0,0 +1,161 @@
import { IAppInsightsDeprecated } from "../src/ApplicationInsightsDeprecated";
import { ApplicationInsightsContainer } from "../src/ApplicationInsightsContainer";
import { Snippet } from "../src/Initialization";
import { Sender } from "@microsoft/applicationinsights-channel-js";
export class ApplicationInsightsDeprecatedTests extends TestClass {
private static readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
private _aiDeprecated: IAppInsightsDeprecated;
private _snippet: Snippet;
// Sinon
private errorSpy: SinonSpy;
private successSpy: SinonSpy;
private loggingSpy: SinonSpy;
private clearSpy: SinonSpy;
private _name = "ApplicationInsightsDeprecatedTests: ";
public testInitialize() {
try {
this.useFakeServer = false;
(<any>sinon.fakeServer).restore();
this.useFakeTimers = false;
this.clock.restore();
this._snippet = {
config: {
instrumentationKey: ApplicationInsightsDeprecatedTests._instrumentationKey,
disableAjaxTracking: false,
disableFetchTracking: false
},
queue: [],
version: 1.0
}
this._aiDeprecated = <IAppInsightsDeprecated>(ApplicationInsightsContainer.getAppInsights(this._snippet, this._snippet.version));
// Setup Sinon stuff
let appInsights = (<any>this._aiDeprecated).appInsightsNew;
const sender: Sender = appInsights.core['_channelController'].channelQueue[0][0];
this.errorSpy = this.sandbox.spy(sender, '_onError');
this.successSpy = this.sandbox.spy(sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(appInsights.core.logger, 'throwInternal');
} catch (e) {
console.error('Failed to initialize');
}
}
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
}
public registerTests() {
this.addApiTests();
this.testCase({
name: 'config.oldApiSupport set to true returns support for 1.0 apis',
test: () => {
Assert.ok(this._aiDeprecated, 'ApplicationInsights SDK exists');
Assert.ok((<IAppInsightsDeprecated>this._aiDeprecated).downloadAndSetup); // has legacy method
}
});
}
public addApiTests(): void {
this.testCaseAsync({
name: this._name + 'ApplicationInsightsDeprecatedTests: trackEvent sends to backend',
stepDelay: 1,
steps: [() => {
this._aiDeprecated.trackEvent('event');
}].concat(this.asserts(1))
});
this.testCaseAsync({
name: this._name + 'trackTrace sends to backend',
stepDelay: 1,
steps: [() => {
this._aiDeprecated.trackTrace('trace');
}].concat(this.asserts(1))
});
this.testCaseAsync({
name: this._name + 'trackException sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
try {
window['a']['b']();
Assert.ok(false, 'trackException test not run');
} catch (e) {
exception = e;
this._aiDeprecated.trackException(exception);
}
Assert.ok(exception);
}].concat(this.asserts(1))
});
this.testCaseAsync({
name: this._name + "track metric",
stepDelay: 1,
steps: [
() => {
console.log("* calling trackMetric " + new Date().toISOString());
for (var i = 0; i < 100; i++) {
this._aiDeprecated.trackMetric("test" + i,Math.round(100 * Math.random()));
}
console.log("* done calling trackMetric " + new Date().toISOString());
}
].concat(this.asserts(100))
});
this.testCaseAsync({
name: this._name + "track page view",
stepDelay: 1,
steps: [
() => {
this._aiDeprecated.trackPageView(); // sends 2
}
].concat(this.asserts(2))
});
}
private boilerPlateAsserts = () => {
Assert.ok(this.successSpy.called, "success");
Assert.ok(!this.errorSpy.called, "no error sending");
var isValidCallCount = this.loggingSpy.callCount === 0;
Assert.ok(isValidCallCount, "logging spy was called 0 time(s)");
if (!isValidCallCount) {
while (this.loggingSpy.args.length) {
Assert.ok(false, "[warning thrown]: " + this.loggingSpy.args.pop());
}
}
}
private asserts: any = (expectedCount: number) => [() => {
var message = "polling: " + new Date().toISOString();
Assert.ok(true, message);
console.log(message);
if (this.successSpy.called) {
this.boilerPlateAsserts();
this.testCleanup();
} else if (this.errorSpy.called || this.loggingSpy.called) {
this.boilerPlateAsserts();
}
},
(PollingAssert.createPollingAssert(() => {
Assert.ok(true, "* checking success spy " + new Date().toISOString());
if(this.successSpy.called) {
let currentCount: number = 0;
this.successSpy.args.forEach(call => {
currentCount += call[1];
});
console.log('curr: ' + currentCount + ' exp: ' + expectedCount);
return currentCount === expectedCount;
} else {
return false;
}
}, "sender succeeded", 30, 1000))];
}

61
vNext/AISKU/tests/External/blanket-1.2.2.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

44
vNext/AISKU/tests/External/blanket-reporter.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,44 @@
(function myReporter() {
var reported = false;
var a = document.createElement("a");
document.body.appendChild(a);
//your reporter code
blanket.customReporter = function (coverage) {
blanket.defaultReporter(coverage);
var styleTags = document.getElementsByTagName("style");
var styles = "";
for (var i = 0; i < styleTags.length; i++) {
styles += styleTags[i].outerHTML;
}
var scriptTags = document.getElementsByTagName("body")[0].getElementsByTagName("script");
var scripts = "";
for (var i = 0; i < scriptTags.length; i++) {
scripts += scriptTags[i].outerHTML;
}
var title = document.getElementsByTagName("title")[0].text;
var documentName = title.replace(/[^\w]/ig, '') + 'Coverage.html';
var coverageReport = '';
coverageReport += '<html>' + '<head>' + styles + "</head><body><h1>" + title.replace(/[^\w\s]/ig, '') + "</h1>" + scripts + document.getElementById("blanket-main").outerHTML + "</body></html>";
var file = new Blob([coverageReport], { type: 'text/plain' });
a.href = URL.createObjectURL(file);
a.download = documentName;
if (!reported) {
a.click();
reported = true;
}
};
})();

10308
vNext/AISKU/tests/External/jquery-1.11.1.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

3926
vNext/AISKU/tests/External/jquery.d.ts поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

4334
vNext/AISKU/tests/External/qunit-1.23.1.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

723
vNext/AISKU/tests/External/qunit.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,723 @@
// Type definitions for QUnit 1.10
// Project: http://qunitjs.com/
// Definitions by: Diullei Gomes <https://github.com/diullei>
// DefinitelyTyped: https://github.com/borisyankov/DefinitelyTyped
interface DoneCallbackObject {
/**
* The number of failed assertions
*/
failed: number;
/**
* The number of passed assertions
*/
passed: number;
/**
* The total number of assertions
*/
total: number;
/**
* The time in milliseconds it took tests to run from start to finish.
*/
runtime: number;
}
interface LogCallbackObject {
/**
* The boolean result of an assertion, true means passed, false means failed.
*/
result: boolean;
/**
* One side of a comparision assertion. Can be undefined when ok() is used.
*/
actual: Object;
/**
* One side of a comparision assertion. Can be undefined when ok() is used.
*/
expected: Object;
/**
* A string description provided by the assertion.
*/
message: string;
/**
* The associated stacktrace, either from an exception or pointing to the source
* of the assertion. Depends on browser support for providing stacktraces, so can be
* undefined.
*/
source: string;
}
interface ModuleStartCallbackObject {
/**
* Name of the next module to run
*/
name: string;
}
interface ModuleDoneCallbackObject {
/**
* Name of this module
*/
name: string;
/**
* The number of failed assertions
*/
failed: number;
/**
* The number of passed assertions
*/
passed: number;
/**
* The total number of assertions
*/
total: number;
}
interface TestDoneCallbackObject {
/**
* TName of the next test to run
*/
name: string;
/**
* Name of the current module
*/
module: string;
/**
* The number of failed assertions
*/
failed: number;
/**
* The number of passed assertions
*/
passed: number;
/**
* The total number of assertions
*/
total: number;
/**
* The total runtime, including setup and teardown
*/
duration: number;
}
interface TestStartCallbackObject {
/**
* Name of the next test to run
*/
name: string;
/**
* Name of the current module
*/
module: string;
}
interface Config {
altertitle: boolean;
autostart: boolean;
current: Object;
reorder: boolean;
requireExpects: boolean;
testTimeout: number;
urlConfig: Array<URLConfigItem>;
done: any;
}
interface URLConfigItem {
id: string;
label: string;
tooltip: string;
}
interface LifecycleObject {
/**
* Runs before each test
*/
setup?: () => any;
/**
* Runs after each test
*/
teardown?: () => any;
}
interface QUnitAssert {
/* ASSERT */
assert: any;
current_testEnvironment: any;
jsDump: any;
/**
* A deep recursive comparison assertion, working on primitive types, arrays, objects,
* regular expressions, dates and functions.
*
* The deepEqual() assertion can be used just like equal() when comparing the value of
* objects, such that { key: value } is equal to { key: value }. For non-scalar values,
* identity will be disregarded by deepEqual.
*
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
deepEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
*
* The equal assertion uses the simple comparison operator (==) to compare the actual
* and expected arguments. When they are equal, the assertion passes: any; otherwise, it fails.
* When it fails, both actual and expected values are displayed in the test result,
* in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
equal(actual: any, expected: any, message?: string): any;
/**
* An inverted deep recursive comparison assertion, working on primitive types,
* arrays, objects, regular expressions, dates and functions.
*
* The notDeepEqual() assertion can be used just like equal() when comparing the
* value of objects, such that { key: value } is equal to { key: value }. For non-scalar
* values, identity will be disregarded by notDeepEqual.
*
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
notDeepEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare
* the actual and expected arguments. When they aren't equal, the assertion passes: any;
* otherwise, it fails. When it fails, both actual and expected values are displayed
* in the test result, in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
notEqual(actual: any, expected: any, message?: string): any;
notPropEqual(actual: any, expected: any, message?: string): any;
propEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notStrictEqual assertion uses the strict inverted comparison operator (!==)
* to compare the actual and expected arguments. When they aren't equal, the assertion
* passes: any; otherwise, it fails. When it fails, both actual and expected values are
* displayed in the test result, in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
notStrictEqual(actual: any, expected: any, message?: string): any;
/**
* A boolean assertion, equivalent to CommonJSs assert.ok() and JUnits assertTrue().
* Passes if the first argument is truthy.
*
* The most basic assertion in QUnit, ok() requires just one argument. If the argument
* evaluates to true, the assertion passes; otherwise, it fails. If a second message
* argument is provided, it will be displayed in place of the result.
*
* @param state Expression being tested
* @param message A short description of the assertion
*/
ok(state: any, message?: string): any;
/**
* A strict type and value comparison assertion.
*
* The strictEqual() assertion provides the most rigid comparison of type and value with
* the strict equality operator (===)
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
strictEqual(actual: any, expected: any, message?: string): any;
/**
* Assertion to test if a callback throws an exception when run.
*
* When testing code that is expected to throw an exception based on a specific set of
* circumstances, use throws() to catch the error object for testing and comparison.
*
* @param block Function to execute
* @param expected Error Object to compare
* @param message A short description of the assertion
*/
throws(block: () => any, expected: any, message?: string): any;
/**
* @param block Function to execute
* @param message A short description of the assertion
*/
throws(block: () => any, message?: string): any;
}
interface QUnitStatic extends QUnitAssert{
/* ASYNC CONTROL */
/**
* Start running tests again after the testrunner was stopped. See stop().
*
* When your async test has multiple exit points, call start() for the corresponding number of stop() increments.
*
* @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls.
*/
start(decrement?: number): any;
/**
* Stop the testrunner to wait for async tests to run. Call start() to continue.
*
* When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need.
*
* On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere.
*
* @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls.
*/
stop(increment? : number): any;
/* CALLBACKS */
/**
* Register a callback to fire whenever the test suite begins.
*
* QUnit.begin() is called once before running any tests. (a better would've been QUnit.start,
* but thats already in use elsewhere and can't be changed.)
*
* @param callback Callback to execute
*/
begin(callback: () => any): any;
/**
* Register a callback to fire whenever the test suite ends.
*
* @param callback Callback to execute.
*/
done(callback: (details: DoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever an assertion completes.
*
* This is one of several callbacks QUnit provides. Its intended for integration scenarios like
* PhantomJS or Jenkins. The properties of the details argument are listed below as options.
*
* @param callback Callback to execute.
*/
log(callback: (details: LogCallbackObject) => any): any;
/**
* Register a callback to fire whenever a module ends.
*
* @param callback Callback to execute.
*/
moduleDone(callback: (details: ModuleDoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever a module begins.
*
* @param callback Callback to execute.
*/
moduleStart(callback: (details: ModuleStartCallbackObject) => any): any;
/**
* Register a callback to fire whenever a test ends.
*
* @param callback Callback to execute.
*/
testDone(callback: (details: TestDoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever a test begins.
*
* @param callback Callback to execute.
*/
testStart(callback: (details: TestStartCallbackObject) => any): any;
/* CONFIGURATION */
/**
* QUnit has a bunch of internal configuration defaults, some of which are
* useful to override. Check the description for each option for details.
*/
config: Config;
/* TEST */
/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
asyncTest(name: string, expected: number, test: () => any): any;
/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param test Function to close over assertions
*/
asyncTest(name: string, test: () => any): any;
/**
* Specify how many assertions are expected to run within a test.
*
* To ensure that an explicit number of assertions are run within any test, use
* expect( number ) to register an expected count. If the number of assertions
* run does not match the expected count, the test will fail.
*
* @param amount Number of assertions in this test.
*/
expect(amount: number): any;
/**
* Group related tests under a single label.
*
* All tests that occur after a call to module() will be grouped into that module.
* The test names will all be preceded by the module name in the test results.
* You can then use that module name to select tests to run.
*
* @param name Label for this group of tests
* @param lifecycle Callbacks to run before and after each test
*/
module(name: string, lifecycle?: LifecycleObject): any;
/**
* Add a test to run.
*
* When testing the most common, synchronous code, use test().
* The assert argument to the callback contains all of QUnit's assertion methods.
* If you are avoiding using any of QUnit's globals, you can use the assert
* argument instead.
*
* @param title Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
test(title: string, expected: number, test: (assert: QUnitAssert) => any): any;
/**
* @param title Title of unit being tested
* @param test Function to close over assertions
*/
test(title: string, test: (assert: QUnitAssert) => any): any;
/**
* https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568
*/
equiv(a: any, b: any): any;
// https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L661
raises: any;
/**
* https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L897
*/
push(result: any, actual: any, expected: any, message: string): any;
/**
* https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L839
*/
reset(): any;
}
/* ASSERT */
/**
* A deep recursive comparison assertion, working on primitive types, arrays, objects,
* regular expressions, dates and functions.
*
* The deepEqual() assertion can be used just like equal() when comparing the value of
* objects, such that { key: value } is equal to { key: value }. For non-scalar values,
* identity will be disregarded by deepEqual.
*
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function deepEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
*
* The equal assertion uses the simple comparison operator (==) to compare the actual
* and expected arguments. When they are equal, the assertion passes: any; otherwise, it fails.
* When it fails, both actual and expected values are displayed in the test result,
* in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function equal(actual: any, expected: any, message?: string): any;
/**
* An inverted deep recursive comparison assertion, working on primitive types,
* arrays, objects, regular expressions, dates and functions.
*
* The notDeepEqual() assertion can be used just like equal() when comparing the
* value of objects, such that { key: value } is equal to { key: value }. For non-scalar
* values, identity will be disregarded by notDeepEqual.
*
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notDeepEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare
* the actual and expected arguments. When they aren't equal, the assertion passes;
* otherwise, it fails. When it fails, both actual and expected values are displayed
* in the test result, in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notEqual(actual: any, expected: any, message?: string): any;
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notStrictEqual assertion uses the strict inverted comparison operator (!==)
* to compare the actual and expected arguments. When they aren't equal, the assertion
* passes; otherwise, it fails. When it fails, both actual and expected values are
* displayed in the test result, in addition to a given message.
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notStrictEqual(actual: any, expected: any, message?: string): any;
/**
* A boolean assertion, equivalent to CommonJSs assert.ok() and JUnits assertTrue().
* Passes if the first argument is truthy.
*
* The most basic assertion in QUnit, ok() requires just one argument. If the argument
* evaluates to true, the assertion passes; otherwise, it fails. If a second message
* argument is provided, it will be displayed in place of the result.
*
* @param state Expression being tested
* @param message A short description of the assertion
*/
declare function ok(state: any, message?: string): any;
/**
* A strict type and value comparison assertion.
*
* The strictEqual() assertion provides the most rigid comparison of type and value with
* the strict equality operator (===)
*
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function strictEqual(actual: any, expected: any, message?: string): any;
/**
* Assertion to test if a callback throws an exception when run.
*
* When testing code that is expected to throw an exception based on a specific set of
* circumstances, use throws() to catch the error object for testing and comparison.
*
* @param block Function to execute
* @param expected Error Object to compare
* @param message A short description of the assertion
*/
declare function throws(block: () => any, expected: any, message?: string): any;
/**
* @param block Function to execute
* @param message A short description of the assertion
*/
declare function throws(block: () => any, message?: string): any;
/* ASYNC CONTROL */
/**
* Start running tests again after the testrunner was stopped. See stop().
*
* When your async test has multiple exit points, call start() for the corresponding number of stop() increments.
*
* @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls.
*/
declare function start(decrement?: number): any;
/**
* Stop the testrunner to wait for async tests to run. Call start() to continue.
*
* When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need.
*
* On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere.
*
* @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls.
*/
declare function stop(increment? : number): any;
/* CALLBACKS */
/**
* Register a callback to fire whenever the test suite begins.
*
* QUnit.begin() is called once before running any tests. (a better would've been QUnit.start,
* but thats already in use elsewhere and can't be changed.)
*
* @param callback Callback to execute
*/
declare function begin(callback: () => any): any;
/**
* Register a callback to fire whenever the test suite ends.
*
* @param callback Callback to execute.
*/
declare function done(callback: (details: DoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever an assertion completes.
*
* This is one of several callbacks QUnit provides. Its intended for integration scenarios like
* PhantomJS or Jenkins. The properties of the details argument are listed below as options.
*
* @param callback Callback to execute.
*/
declare function log(callback: (details: LogCallbackObject) => any): any;
/**
* Register a callback to fire whenever a module ends.
*
* @param callback Callback to execute.
*/
declare function moduleDone(callback: (details: ModuleDoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever a module begins.
*
* @param callback Callback to execute.
*/
declare function moduleStart(callback: (name: string) => any): any;
/**
* Register a callback to fire whenever a test ends.
*
* @param callback Callback to execute.
*/
declare function testDone(callback: (details: TestDoneCallbackObject) => any): any;
/**
* Register a callback to fire whenever a test begins.
*
* @param callback Callback to execute.
*/
declare function testStart(callback: (details: TestStartCallbackObject) => any): any;
/* TEST */
/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
declare function asyncTest(name: string, expected?: any, test?: () => any): any;
/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param test Function to close over assertions
*/
declare function asyncTest(name: string, test: () => any): any;
/**
* Specify how many assertions are expected to run within a test.
*
* To ensure that an explicit number of assertions are run within any test, use
* expect( number ) to register an expected count. If the number of assertions
* run does not match the expected count, the test will fail.
*
* @param amount Number of assertions in this test.
*/
declare function expect(amount: number): any;
// ** conflict with TypeScript module keyword. Must be used on QUnit namespace
//declare var module: (name: string, lifecycle?: LifecycleObject) => any;
/**
* Add a test to run.
*
* When testing the most common, synchronous code, use test().
* The assert argument to the callback contains all of QUnit's assertion methods.
* If you are avoiding using any of QUnit's globals, you can use the assert
* argument instead.
*
* @param title Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
declare function test(title: string, expected: number, test: (assert?: QUnitAssert) => any): any;
/**
* @param title Title of unit being tested
* @param test Function to close over assertions
*/
declare function test(title: string, test: (assert?: QUnitAssert) => any): any;
declare function notPropEqual(actual: any, expected: any, message?: string): any;
declare function propEqual(actual: any, expected: any, message?: string): any;
// https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568
declare function equiv(a: any, b: any): any;
// https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L661
declare var raises: any;
/* QUNIT */
declare var QUnit: QUnitStatic;

6389
vNext/AISKU/tests/External/sinon-1.17.2.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

434
vNext/AISKU/tests/External/sinon.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,434 @@
// Type definitions for Sinon 1.16.0
// Project: http://sinonjs.org/
// Definitions by: William Sears <https://github.com/mrbigdog2u>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface SinonSpyCallApi {
// Properties
thisValue: any;
args: any[];
exception: any;
returnValue: any;
// Methods
calledOn(obj: any): boolean;
calledWith(...args: any[]): boolean;
calledWithExactly(...args: any[]): boolean;
calledWithMatch(...args: any[]): boolean;
notCalledWith(...args: any[]): boolean;
notCalledWithMatch(...args: any[]): boolean;
returned(value: any): boolean;
threw(): boolean;
threw(type: string): boolean;
threw(obj: any): boolean;
callArg(pos: number): void;
callArgOn(pos: number, obj: any, ...args: any[]): void;
callArgWith(pos: number, ...args: any[]): void;
callArgOnWith(pos: number, obj: any, ...args: any[]): void;
yield(...args: any[]): void;
yieldOn(obj: any, ...args: any[]): void;
yieldTo(property: string, ...args: any[]): void;
yieldToOn(property: string, obj: any, ...args: any[]): void;
}
interface SinonSpyCall extends SinonSpyCallApi {
calledBefore(call: SinonSpyCall): boolean;
calledAfter(call: SinonSpyCall): boolean;
calledWithNew(call: SinonSpyCall): boolean;
}
interface SinonSpy extends SinonSpyCallApi {
// Properties
callCount: number;
called: boolean;
notCalled: boolean;
calledOnce: boolean;
calledTwice: boolean;
calledThrice: boolean;
firstCall: SinonSpyCall;
secondCall: SinonSpyCall;
thirdCall: SinonSpyCall;
lastCall: SinonSpyCall;
thisValues: any[];
args: any[][];
exceptions: any[];
returnValues: any[];
// Methods
(...args: any[]): any;
calledBefore(anotherSpy: SinonSpy): boolean;
calledAfter(anotherSpy: SinonSpy): boolean;
calledWithNew(spy: SinonSpy): boolean;
withArgs(...args: any[]): SinonSpy;
alwaysCalledOn(obj: any): boolean;
alwaysCalledWith(...args: any[]): boolean;
alwaysCalledWithExactly(...args: any[]): boolean;
alwaysCalledWithMatch(...args: any[]): boolean;
neverCalledWith(...args: any[]): boolean;
neverCalledWithMatch(...args: any[]): boolean;
alwaysThrew(): boolean;
alwaysThrew(type: string): boolean;
alwaysThrew(obj: any): boolean;
alwaysReturned(): boolean;
invokeCallback(...args: any[]): void;
getCall(n: number): SinonSpyCall;
reset(): void;
printf(format: string, ...args: any[]): string;
restore(): void;
}
interface SinonSpyStatic {
(): SinonSpy;
(func: any): SinonSpy;
(obj: any, method: string): SinonSpy;
}
interface SinonStatic {
spy: SinonSpyStatic;
}
interface SinonStub extends SinonSpy {
resetBehavior(): void;
returns(obj: any): SinonStub;
returnsArg(index: number): SinonStub;
throws(type?: string): SinonStub;
throws(obj: any): SinonStub;
callsArg(index: number): SinonStub;
callsArgOn(index: number, context: any): SinonStub;
callsArgWith(index: number, ...args: any[]): SinonStub;
callsArgOnWith(index: number, context: any, ...args: any[]): SinonStub;
callsArgAsync(index: number): SinonStub;
callsArgOnAsync(index: number, context: any): SinonStub;
callsArgWithAsync(index: number, ...args: any[]): SinonStub;
callsArgOnWithAsync(index: number, context: any, ...args: any[]): SinonStub;
onCall(n: number): SinonStub;
onFirstCall(): SinonStub;
onSecondCall(): SinonStub;
onThirdCall(): SinonStub;
yields(...args: any[]): SinonStub;
yieldsOn(context: any, ...args: any[]): SinonStub;
yieldsTo(property: string, ...args: any[]): SinonStub;
yieldsToOn(property: string, context: any, ...args: any[]): SinonStub;
yieldsAsync(...args: any[]): SinonStub;
yieldsOnAsync(context: any, ...args: any[]): SinonStub;
yieldsToAsync(property: string, ...args: any[]): SinonStub;
yieldsToOnAsync(property: string, context: any, ...args: any[]): SinonStub;
withArgs(...args: any[]): SinonStub;
}
interface SinonStubStatic {
(): SinonStub;
(obj: any): SinonStub;
(obj: any, method: string): SinonStub;
(obj: any, method: string, func: any): SinonStub;
}
interface SinonStatic {
stub: SinonStubStatic;
}
interface SinonExpectation extends SinonStub {
atLeast(n: number): SinonExpectation;
atMost(n: number): SinonExpectation;
never(): SinonExpectation;
once(): SinonExpectation;
twice(): SinonExpectation;
thrice(): SinonExpectation;
exactly(n: number): SinonExpectation;
withArgs(...args: any[]): SinonExpectation;
withExactArgs(...args: any[]): SinonExpectation;
on(obj: any): SinonExpectation;
verify(): SinonExpectation;
restore(): void;
}
interface SinonExpectationStatic {
create(methodName?: string): SinonExpectation;
}
interface SinonMock {
expects(method: string): SinonExpectation;
restore(): void;
verify(): void;
}
interface SinonMockStatic {
(): SinonExpectation;
(obj: any): SinonMock;
}
interface SinonStatic {
expectation: SinonExpectationStatic;
mock: SinonMockStatic;
}
interface SinonFakeTimers {
now: number;
create(now: number): SinonFakeTimers;
setTimeout(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number;
clearTimeout(id: number): void;
setInterval(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number;
clearInterval(id: number): void;
tick(ms: number): number;
reset(): void;
Date(): Date;
Date(year: number): Date;
Date(year: number, month: number): Date;
Date(year: number, month: number, day: number): Date;
Date(year: number, month: number, day: number, hour: number): Date;
Date(year: number, month: number, day: number, hour: number, minute: number): Date;
Date(year: number, month: number, day: number, hour: number, minute: number, second: number): Date;
Date(year: number, month: number, day: number, hour: number, minute: number, second: number, ms: number): Date;
restore(): void;
/**
* Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp
* without affecting timers, intervals or immediates.
* @param now The new 'now' in unix milliseconds
*/
setSystemTime(now: number): void;
/**
* Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp
* without affecting timers, intervals or immediates.
* @param now The new 'now' as a JavaScript Date
*/
setSystemTime(date: Date): void;
}
interface SinonFakeTimersStatic {
(): SinonFakeTimers;
(...timers: string[]): SinonFakeTimers;
(now: number, ...timers: string[]): SinonFakeTimers;
}
interface SinonStatic {
useFakeTimers: SinonFakeTimersStatic;
clock: SinonFakeTimers;
}
interface SinonFakeUploadProgress {
eventListeners: {
progress: any[];
load: any[];
abort: any[];
error: any[];
};
addEventListener(event: string, listener: (e: Event) => any): void;
removeEventListener(event: string, listener: (e: Event) => any): void;
dispatchEvent(event: Event): void;
}
interface SinonFakeXMLHttpRequest {
// Properties
onCreate: (xhr: SinonFakeXMLHttpRequest) => void;
url: string;
method: string;
requestHeaders: any;
requestBody: string;
status: number;
statusText: string;
async: boolean;
username: string;
password: string;
withCredentials: boolean;
upload: SinonFakeUploadProgress;
responseXML: Document;
getResponseHeader(header: string): string;
getAllResponseHeaders(): any;
// Methods
restore(): void;
useFilters: boolean;
addFilter(filter: (method: string, url: string, async: boolean, username: string, password: string) => boolean): void;
setResponseHeaders(headers: any): void;
setResponseBody(body: string): void;
respond(status: number, headers: any, body: string): void;
autoRespond(ms: number): void;
}
interface SinonFakeXMLHttpRequestStatic {
(): SinonFakeXMLHttpRequest;
}
interface SinonStatic {
useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic;
FakeXMLHttpRequest: SinonFakeXMLHttpRequest;
}
interface SinonFakeServer {
// Properties
autoRespond: boolean;
autoRespondAfter: number;
fakeHTTPMethods: boolean;
getHTTPMethod: (request: SinonFakeXMLHttpRequest) => string;
requests: SinonFakeXMLHttpRequest[];
respondImmediately: boolean;
// Methods
respondWith(body: string): void;
respondWith(response: any[]): void;
respondWith(fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
respondWith(url: string, body: string): void;
respondWith(url: string, response: any[]): void;
respondWith(url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
respondWith(method: string, url: string, body: string): void;
respondWith(method: string, url: string, response: any[]): void;
respondWith(method: string, url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
respondWith(url: RegExp, body: string): void;
respondWith(url: RegExp, response: any[]): void;
respondWith(url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
respondWith(method: string, url: RegExp, body: string): void;
respondWith(method: string, url: RegExp, response: any[]): void;
respondWith(method: string, url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
respond(): void;
restore(): void;
}
interface SinonFakeServerStatic {
create(): SinonFakeServer;
}
interface SinonStatic {
fakeServer: SinonFakeServerStatic;
fakeServerWithClock: SinonFakeServerStatic;
}
interface SinonExposeOptions {
prefix?: string;
includeFail?: boolean;
}
interface SinonAssert {
// Properties
failException: string;
fail: (message?: string) => void; // Overridable
pass: (assertion: any) => void; // Overridable
// Methods
notCalled(spy: SinonSpy): void;
called(spy: SinonSpy): void;
calledOnce(spy: SinonSpy): void;
calledTwice(spy: SinonSpy): void;
calledThrice(spy: SinonSpy): void;
callCount(spy: SinonSpy, count: number): void;
callOrder(...spies: SinonSpy[]): void;
calledOn(spy: SinonSpy, obj: any): void;
alwaysCalledOn(spy: SinonSpy, obj: any): void;
calledWith(spy: SinonSpy, ...args: any[]): void;
alwaysCalledWith(spy: SinonSpy, ...args: any[]): void;
neverCalledWith(spy: SinonSpy, ...args: any[]): void;
calledWithExactly(spy: SinonSpy, ...args: any[]): void;
alwaysCalledWithExactly(spy: SinonSpy, ...args: any[]): void;
calledWithMatch(spy: SinonSpy, ...args: any[]): void;
alwaysCalledWithMatch(spy: SinonSpy, ...args: any[]): void;
neverCalledWithMatch(spy: SinonSpy, ...args: any[]): void;
threw(spy: SinonSpy): void;
threw(spy: SinonSpy, exception: string): void;
threw(spy: SinonSpy, exception: any): void;
alwaysThrew(spy: SinonSpy): void;
alwaysThrew(spy: SinonSpy, exception: string): void;
alwaysThrew(spy: SinonSpy, exception: any): void;
expose(obj: any, options?: SinonExposeOptions): void;
}
interface SinonStatic {
assert: SinonAssert;
}
interface SinonMatcher {
and(expr: SinonMatcher): SinonMatcher;
or(expr: SinonMatcher): SinonMatcher;
}
interface SinonMatch {
(value: number): SinonMatcher;
(value: string): SinonMatcher;
(expr: RegExp): SinonMatcher;
(obj: any): SinonMatcher;
(callback: (value: any) => boolean): SinonMatcher;
any: SinonMatcher;
defined: SinonMatcher;
truthy: SinonMatcher;
falsy: SinonMatcher;
bool: SinonMatcher;
number: SinonMatcher;
string: SinonMatcher;
object: SinonMatcher;
func: SinonMatcher;
array: SinonMatcher;
regexp: SinonMatcher;
date: SinonMatcher;
same(obj: any): SinonMatcher;
typeOf(type: string): SinonMatcher;
instanceOf(type: any): SinonMatcher;
has(property: string, expect?: any): SinonMatcher;
hasOwn(property: string, expect?: any): SinonMatcher;
}
interface SinonStatic {
match: SinonMatch;
}
interface SinonSandboxConfig {
injectInto?: any;
properties?: string[];
useFakeTimers?: any;
useFakeServer?: any;
}
interface SinonSandbox {
clock: SinonFakeTimers;
requests: SinonFakeXMLHttpRequest;
server: SinonFakeServer;
spy: SinonSpyStatic;
stub: SinonStubStatic;
mock: SinonMockStatic;
useFakeTimers: SinonFakeTimersStatic;
useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic;
useFakeServer(): SinonFakeServer;
restore(): void;
}
interface SinonSandboxStatic {
create(): SinonSandbox;
create(config: SinonSandboxConfig): SinonSandbox;
}
interface SinonStatic {
sandbox: SinonSandboxStatic;
}
interface SinonTestConfig {
injectIntoThis?: boolean;
injectInto?: any;
properties?: string[];
useFakeTimers?: boolean;
useFakeServer?: boolean;
}
interface SinonTestWrapper extends SinonSandbox {
(...args: any[]): any;
}
interface SinonStatic {
config: SinonTestConfig;
test(fn: (...args: any[]) => any): SinonTestWrapper;
testCase(tests: any): any;
}
// Utility overridables
interface SinonStatic {
createStubInstance(constructor: any): SinonStub;
format(obj: any): string;
log(message: string): void;
restore(object: any): void;
}
declare var sinon: SinonStatic;
declare module "sinon" {
export = sinon;
}

Просмотреть файл

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-control" content="no-Cache" />
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-1.23.1.css">
<script src="http://sinonjs.org/releases/sinon-1.17.2.js"></script>
<title>Tests for Application Insights JavaScript API</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script>
require.config({
baseUrl: '../../',
paths: {
qunit: "Tests/External/qunit-1.23.1",
tests: 'Tests/appinsights-sdk.tests'
}
});
require([
'qunit',
'node_modules/@microsoft/applicationinsights-core-js/browser/applicationinsights-core-js',
], function(QUnit, core) {
define('@microsoft/applicationinsights-core-js', function(){return core;})
require([
'node_modules/@microsoft/applicationinsights-common/browser/applicationinsights-common'
], function(common) {
define('@microsoft/applicationinsights-common', common);
require([
'node_modules/@microsoft/applicationinsights-analytics-js/browser/applicationinsights-analytics-js',
'node_modules/@microsoft/applicationinsights-channel-js/browser/applicationinsights-channel-js',
'node_modules/@microsoft/applicationinsights-properties-js/browser/applicationinsights-properties-js',
'node_modules/@microsoft/applicationinsights-dependencies-js/browser/applicationinsights-dependencies-js'
], function(analytics, channel, properties, dependencies) {
define('@microsoft/applicationinsights-analytics-js', function(){return analytics;});
define('@microsoft/applicationinsights-channel-js', function() {return channel;});
define('@microsoft/applicationinsights-properties-js', function() {return properties;});
define('@microsoft/applicationinsights-dependencies-js', function() {return dependencies;});
require([
'Tests/Selenium/appinsights-sdk.tests'
], function(tests) {
QUnit.start();
},
function(err) {
console.log(err);
});
});
});
});
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
import { ApplicationInsightsTests } from '../applicationinsights.e2e.tests';
import { SanitizerE2ETests } from '../sanitizer.e2e.tests';
import { ValidateE2ETests } from '../validate.e2e.tests';
import { SenderE2ETests } from '../sender.e2e.tests';
import { ApplicationInsightsDeprecatedTests } from '../ApplicationInsightsDeprecatedTests';
new ApplicationInsightsTests().registerTests();
new ApplicationInsightsDeprecatedTests().registerTests();
new SanitizerE2ETests().registerTests();
new ValidateE2ETests().registerTests();
new SenderE2ETests().registerTests();

Просмотреть файл

@ -0,0 +1,146 @@
/// <reference path="../External/qunit.d.ts" />
/** Wrapper around QUnit asserts. This class has two purposes:
* - Make Assertion methods easy to discover.
* - Make them consistent with XUnit assertions in the order of the actual and expected parameter values.
*/
class Assert {
/**
* A deep recursive comparison assertion, working on primitive types, arrays, objects,
* regular expressions, dates and functions.
*
* The deepEqual() assertion can be used just like equal() when comparing the value of
* objects, such that { key: value } is equal to { key: value }. For non-scalar values,
* identity will be disregarded by deepEqual.
*
* @param expected Known comparison value
* @param actual Object or Expression being tested
* @param message A short description of the assertion
*/
public static deepEqual(expected: any, actual: any, message?: string): any {
return deepEqual(actual, expected, message);
}
/**
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
*
* The equal assertion uses the simple comparison operator (==) to compare the actual
* and expected arguments. When they are equal, the assertion passes: any; otherwise, it fails.
* When it fails, both actual and expected values are displayed in the test result,
* in addition to a given message.
*
* @param expected Known comparison value
* @param actual Expression being tested
* @param message A short description of the assertion
*/
public static equal(expected: any, actual: any, message?: string): any {
return equal(actual, expected, message);
}
/**
* An inverted deep recursive comparison assertion, working on primitive types,
* arrays, objects, regular expressions, dates and functions.
*
* The notDeepEqual() assertion can be used just like equal() when comparing the
* value of objects, such that { key: value } is equal to { key: value }. For non-scalar
* values, identity will be disregarded by notDeepEqual.
*
* @param expected Known comparison value
* @param actual Object or Expression being tested
* @param message A short description of the assertion
*/
public static notDeepEqual(expected: any, actual: any, message?: string): any {
return notDeepEqual(actual, expected, message);
}
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare
* the actual and expected arguments. When they aren't equal, the assertion passes: any;
* otherwise, it fails. When it fails, both actual and expected values are displayed
* in the test result, in addition to a given message.
*
* @param expected Known comparison value
* @param actual Expression being tested
* @param message A short description of the assertion
*/
public static notEqual(expected: any, actual: any, message?: string): any {
return notEqual(actual, expected, message);
}
public static notPropEqual(expected: any, actual: any, message?: string): any {
return notPropEqual(actual, expected, message);
}
public static propEqual(expected: any, actual: any, message?: string): any {
return propEqual(actual, expected, message);
}
/**
* A non-strict comparison assertion, checking for inequality.
*
* The notStrictEqual assertion uses the strict inverted comparison operator (!==)
* to compare the actual and expected arguments. When they aren't equal, the assertion
* passes: any; otherwise, it fails. When it fails, both actual and expected values are
* displayed in the test result, in addition to a given message.
*
* @param expected Known comparison value
* @param actual Expression being tested
* @param message A short description of the assertion
*/
public static notStrictEqual(expected: any, actual: any, message?: string): any {
return notStrictEqual(actual, expected, message);
}
/**
* A boolean assertion, equivalent to CommonJS's assert.ok() and JUnit's assertTrue().
* Passes if the first argument is truthy.
*
* The most basic assertion in QUnit, ok() requires just one argument. If the argument
* evaluates to true, the assertion passes; otherwise, it fails. If a second message
* argument is provided, it will be displayed in place of the result.
*
* @param state Expression being tested
* @param message A short description of the assertion
*/
public static ok(state: any, message?: string): any {
return ok(state, message);
}
/**
* A strict type and value comparison assertion.
*
* The strictEqual() assertion provides the most rigid comparison of type and value with
* the strict equality operator (===)
*
* @param expected Known comparison value
* @param actual Expression being tested
* @param message A short description of the assertion
*/
public static strictEqual(expected: any, actual: any, message?: string): any {
return strictEqual(actual, expected, message);
}
/**
* Assertion to test if a callback throws an exception when run.
*
* When testing code that is expected to throw an exception based on a specific set of
* circumstances, use throws() to catch the error object for testing and comparison.
*
* @param block Function to execute
* @param expected Error Object to compare
* @param message A short description of the assertion
*/
public static throws(block: () => any, expected: any, message?: string): any;
/**
* @param block Function to execute
* @param message A short description of the assertion
*/
public static throws(block: () => any, message?: string): any;
public static throws(block: () => any, expected?: any, message?: string): any {
return throws(block, expected, message);
}
}

Просмотреть файл

@ -0,0 +1,6 @@
/// <reference path="../External/sinon.d.ts" />
/// <reference path="../External/qunit.d.ts" />
/// <reference path="Assert.ts" />
/// <reference path="PollingAssert.ts" />
/// <reference path="TestClass.ts" />
/// <reference path="TestCase.ts" />

Просмотреть файл

@ -0,0 +1,146 @@
/// <reference path="../../JavaScriptSDK/Serializer.ts" />
/// <reference path="./TestClass.ts"/>
class ContractTestHelper extends TestClass {
public name: string;
private initializer: () => Microsoft.ApplicationInsights.ISerializable;
constructor(initializer: () => Microsoft.ApplicationInsights.ISerializable, name: string) {
super();
this.name = name;
this.initializer = initializer;
}
/** Method called before the start of each test method */
public testInitialize() {
}
/** Method called after each test method has completed */
public testCleanup() {
}
public registerTests() {
var name = this.name + ": ";
this.testCase({
name: name + "constructor does not throw errors",
test: () => {
this.getSubject(this.initializer, this.name);
}
});
this.testCase({
name: name + "serialization does not throw errors",
test: () => {
var subject = this.getSubject(this.initializer, this.name);
this.serialize(subject, this.name);
}
});
this.testCase({
name: name + "all required fields are constructed",
test: () => {
this.allRequiredFieldsAreConstructed(this.initializer, this.name);
}
});
this.testCase({
name: name + "extra fields are removed upon serialization",
test: () => {
this.extraFieldsAreRemovedBySerializer(this.initializer, this.name);
}
});
this.testCase({
name: this.name + "optional fields are not required by the back end",
test: () => {
this.optionalFieldsAreNotRequired(this.initializer, this.name);
}
});
this.testCase({
name: this.name + "all fields are serialized if included",
test: () => {
this.allFieldsAreIncludedIfSpecified(this.initializer, this.name);
}
});
}
public checkSerializableObject(initializer: () => any, name: string) {
this.allRequiredFieldsAreConstructed(initializer, name);
this.extraFieldsAreRemovedBySerializer(initializer, name);
this.allFieldsAreIncludedIfSpecified(initializer, name);
}
private allRequiredFieldsAreConstructed(initializer: () => any, name: string) {
var subject = this.getSubject(initializer, name);
for (var field in subject.aiDataContract) {
if (subject.aiDataContract[field] & Microsoft.ApplicationInsights.FieldType.Required) {
Assert.ok(subject[field] != null, "The required field '" + field + "' is constructed for: '" + name + "'");
}
}
}
private extraFieldsAreRemovedBySerializer(initializer: () => any, name: string) {
var subject = this.getSubject(initializer, name);
var extra = "extra";
subject[extra + 0] = extra;
subject[extra + 1] = extra;
subject[extra + 3] = extra;
var serializedSubject = this.serialize(subject, name);
for (var field in serializedSubject) {
Assert.ok(subject.aiDataContract[field] != null, "The field '" + field + "' exists in the contract for '" + name + "' and was serialized");
}
}
private optionalFieldsAreNotRequired(initializer: () => any, name: string) {
var subject = this.getSubject(this.initializer, this.name);
for (var field in subject.aiDataContract) {
if (!subject.aiDataContract[field]) {
delete subject[field];
}
}
}
private allFieldsAreIncludedIfSpecified(initializer: () => any, name: string) {
var subject = this.getSubject(this.initializer, this.name);
for (var field in subject.aiDataContract) {
subject[field] = field;
}
var serializedSubject = this.serialize(subject, this.name);
for (field in subject.aiDataContract) {
Assert.ok(serializedSubject[field] === field, "Field '" + field + "' was not serialized" + this.name);
}
for (field in serializedSubject) {
Assert.ok(subject.aiDataContract[field] !== undefined, "Field '" + field + "' was included but is not specified in the contract " + this.name);
}
}
private serialize(subject: Microsoft.ApplicationInsights.ISerializable, name: string) {
var serialized = "";
try {
serialized = Microsoft.ApplicationInsights.Serializer.serialize(subject);
} catch (e) {
Assert.ok(false, "Failed to serialize '" + name + "'\r\n" + e);
}
return JSON.parse(serialized);
}
private getSubject(construction: () => Microsoft.ApplicationInsights.ISerializable, name: string): any {
var subject = construction();
Assert.ok(!!subject, "can construct " + name);
return subject;
}
}

Просмотреть файл

@ -0,0 +1,227 @@
/// <reference path="../TestFramework/Common.ts" />
/// <reference path="../External/jquery.d.ts" />
/// <reference path="../../JavaScriptSDK/AppInsights.ts" />
/// <reference path="../../JavaScriptSDK/Telemetry/Common/Data.ts" />
interface IJSLitmus {
test: (name: string, f: Function) => void;
stop: () => void;
runAll: (e?: Event) => JQueryDeferred<void>;
_tests: any[];
}
interface IPerfResult {
operationCount: number;
timeInMs: number;
name: string;
opsPerSec: number;
period: number;
date: number;
platform: string;
os: string;
oneHrDate: number;
friendlyDate: string;
group: string;
millisecondsPerOp: number;
microsecondsPerOp: number;
secondsPerOp: number;
browser: string;
}
declare var JSLitmus: IJSLitmus;
class PerformanceTestHelper extends TestClass {
public testCount;
public appInsights;
public testProperties;
public testMeasurements;
public results: IPerfResult[];
private isDone;
constructor(timeout?: number) {
super();
this.testCount = 0;
this.synchronouslyLoadJquery();
this.results = [];
}
/** Method called before the start of each test method */
public testInitialize() {
this.useFakeServer = false;
sinon.fakeServer["restore"]();
this.useFakeTimers = false;
this.clock.restore();
this.appInsights = new Microsoft.ApplicationInsights.AppInsights(<any>{
instrumentationKey: "3e6a441c-b52b-4f39-8944-f81dd6c2dc46",
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
endpointUrl: "https://dc.services.visualstudio.com/v2/track",
maxBatchInterval: 0
});
this.appInsights.context._sender._sender = () => null;
this.testProperties = { p1: "val", p2: "val", p3: "val", p4: "val", p5: "val", p6: "val", p7: "val" };
this.testMeasurements = { m1: 1, m2: 1, m3: 1, m4: 1, m5: 1, m6: 1, m7: 1, m8: 1, m9: 1 };
}
/** Method called after each test method has completed */
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
var serializedPerfResults: string = window["perfResults"] || "[]";
var perfResults: IPerfResult[] = <any>(JSON.parse(serializedPerfResults));
perfResults = perfResults.concat(this.results);
window["perfResults"] = JSON.stringify(perfResults);
window["perfResultsCsv"] = this.toCsv(perfResults).csv;
window["perfResultsCsvHeaders"] = this.toCsv(perfResults).headers;
}
private toCsv(array: any[]) {
var headers = "";
if (array.length > 0) {
var names = [];
for (var name in array[0]) {
names.push(name);
}
headers = names.join(",");
}
var csv = [];
for (var i = 0; i < array.length; i++) {
var datum = array[i];
var values = [];
for (var j = 0; j < names.length; j++) {
values.push(datum[names[j]]);
}
csv.push(values.join(","));
}
return { headers: headers, csv: csv.join("\r\n") };
}
public enqueueTest(name: string, action: () => void) {
JSLitmus.test(name, (count) => {
while (count--) {
action();
}
});
}
public runTests() {
JSLitmus.runAll().done(() => this.onTestsComplete());
}
public onTestsComplete() {
var perfLogging = new Microsoft.ApplicationInsights.AppInsights(<any>{
instrumentationKey: "1a6933ad-f260-447f-a2b0-e2233f6658eb",
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
endpointUrl: "http://prodintdataforker.azurewebsites.net/dcservices?intKey=4d93aad0-cf1d-45b7-afc9-14f55504f6d5",
sessionRenewalMs: 30 * 60 * 1000,
sessionExpirationMs: 24 * 60 * 60 * 1000,
maxBatchSizeInBytes: 1000000,
maxBatchInterval: 0
});
perfLogging.context._sender._sender = (payload) => {
var xhr = new sinon["xhr"].workingXHR();
xhr.open("POST", perfLogging.config.endpointUrl, true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(payload);
}
JSLitmus.stop();
for (var i = 0; i < JSLitmus._tests.length; i++) {
var test = JSLitmus._tests[i];
var opsPerSec = test.count / test.time;
Assert.ok(true, test.name + " operations per sec:" + opsPerSec);
var timeInMs = <number>test.time;
var date = +new Date;
var oneHr = 60 * 60 * 1000;
var oneHrDate = Math.floor(date / oneHr) * oneHr;
var friendlyDate = new Date(oneHrDate).toISOString();
var platform = <string>test.platform;
var browser = "internetExplorer";
var name = <string>test.name;
var group = name.split(".")[0];
if (platform.toLowerCase().indexOf("chrome") >= 0) {
browser = "chrome";
} else if (platform.toLowerCase().indexOf("firefox") >= 0) {
browser = "firefox";
} else if (platform.toLowerCase().indexOf("safari") >= 0) {
browser = "safari";
}
var result: IPerfResult = {
name: name,
timeInMs: timeInMs,
operationCount: 1,
opsPerSec: 1 / (timeInMs / 1000),
period: 1,
date: date,
oneHrDate: oneHrDate,
friendlyDate: friendlyDate,
group: group,
platform: platform,
browser: browser,
os: <string>test.os,
millisecondsPerOp: (timeInMs / 1),
microsecondsPerOp: (timeInMs / 1) * 1000,
secondsPerOp: (timeInMs / 1) / 1000
};
perfLogging.trackMetric(result.name, opsPerSec);
var event = new Microsoft.ApplicationInsights.Telemetry.Event(result.name, opsPerSec, result);
var data = new Microsoft.ApplicationInsights.Telemetry.Common.Data<Microsoft.ApplicationInsights.Telemetry.Event>(
Microsoft.ApplicationInsights.Telemetry.Event.dataType, event);
var envelope = new Microsoft.ApplicationInsights.Telemetry.Common.Envelope(data, Microsoft.ApplicationInsights.Telemetry.Event.envelopeType);
perfLogging.context.track(envelope);
this.results.push(result);
}
JSLitmus._tests.length = 0;
this.isDone = true;
this.testCleanup();
}
public onTimeout() {
if (!this.isDone) {
Assert.ok(false, "timeout reached");
this.onTestsComplete();
}
}
/**
* Synchronously loads jquery
* we could regress the test suite and develop sublte jquery dependencies in the product code
* if jquery were added to all tests as it hides a lot of cross browser weirdness. However,
* for these tests it is useful to manipulate the dom to display performance results.
*/
private synchronouslyLoadJquery() {
if (!window["$"]) {
// get some kind of XMLHttpRequest
var xhrObj = <any>false;
if (window["ActiveXObject"]) {
xhrObj = <any>new ActiveXObject("Microsoft.XMLHTTP");
} else if (window["XMLHttpRequest"]) {
xhrObj = <any>new XMLHttpRequest();
} else {
alert("Please upgrade your browser! Your browser does not support AJAX!");
}
// open and send a synchronous request
xhrObj.open('GET', "http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js", false);
xhrObj.send('');
// add the returned content to a newly created script tag
var script = document.createElement('script');
script.type = "text/javascript";
script.text = xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(script);
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
/// <reference path="../External/qunit.d.ts" />
/// <reference path="TestClass.ts" />
class PollingAssert {
/**
* Starts polling assertion function for a period of time after which it's considered failed.
* @param {() => boolean} assertionFunctionReturnsBoolean - funciton returning true if condition passes and false if condition fails. Assertion will be done on this function's result.
* @param {string} assertDescription - message shown with the assertion
* @param {number} timeoutSeconds - timeout in seconds after which assertion fails
* @param {number} pollIntervalMs - polling interval in milliseconds
* @returns {(nextTestStep) => void} callback which will be invoked by the TestClass
*/
public static createPollingAssert(assertionFunctionReturnsBoolean: () => boolean, assertDescription: string, timeoutSeconds: number = 30, pollIntervalMs: number = 500): (nextTestStep) => void {
var pollingAssert = (nextTestStep) => {
var timeout = new Date(new Date().getTime() + timeoutSeconds * 1000);
var polling = () => {
if (assertionFunctionReturnsBoolean.apply(this)) {
Assert.ok(true, assertDescription);
nextTestStep();
} else if (timeout < new Date()) {
Assert.ok(false, "assert didn't succeed for " + timeout + " seconds: " + assertDescription);
nextTestStep();
} else {
setTimeout(polling, pollIntervalMs);
}
}
setTimeout(polling, pollIntervalMs);
}
pollingAssert[TestClass.isPollingStepFlag] = true;
return pollingAssert;
}
}

Просмотреть файл

@ -0,0 +1,22 @@

/** Defines a test case */
class TestCase {
/** Name to use for the test case */
public name: string;
/** Test case method */
public test: () => void;
}
/** Defines a test case */
interface TestCaseAsync {
/** Name to use for the test case */
name: string;
/** time to wait after pre before invoking post and calling start() */
stepDelay: number;
/** async steps */
steps: Array<() => void>;
}

Просмотреть файл

@ -0,0 +1,250 @@
/// <reference path="../External/sinon.d.ts" />
/// <reference path="../External/qunit.d.ts" />
/// <reference path="Assert.ts" />
/// <reference path="./TestCase.ts"/>
class TestClass {
constructor(name?: string) {
QUnit.module(name);
}
public static isPollingStepFlag = "isPollingStep";
/** The instance of the currently running suite. */
public static currentTestClass: TestClass;
/** Turns on/off sinon's syncronous implementation of setTimeout. On by default. */
public useFakeTimers: boolean = true;
/** Turns on/off sinon's fake implementation of XMLHttpRequest. On by default. */
public useFakeServer: boolean = true;
/** Method called before the start of each test method */
public testInitialize() {
}
/** Method called after each test method has completed */
public testCleanup() {
}
/** Method in which test class intances should call this.testCase(...) to register each of this suite's tests. */
public registerTests() {
}
/** Register an async Javascript unit testcase. */
public testCaseAsync(testInfo: TestCaseAsync) {
if (!testInfo.name) {
throw new Error("Must specify name in testInfo context in registerTestcase call");
}
if (isNaN(testInfo.stepDelay)) {
throw new Error("Must specify 'stepDelay' period between pre and post");
}
if (!testInfo.steps) {
throw new Error("Must specify 'steps' to take asynchronously");
}
// Create a wrapper around the test method so we can do test initilization and cleanup.
var testMethod = (assert) => {
var done = assert.async();
// Save off the instance of the currently running suite.
TestClass.currentTestClass = this;
// Run the test.
try {
this._testStarting();
var steps = testInfo.steps;
var trigger = () => {
if (steps.length) {
var step = steps.shift();
// The callback which activates the next test step.
var nextTestStepTrigger = () => {
setTimeout(() => {
trigger();
}, testInfo.stepDelay);
};
// There 2 types of test steps - simple and polling.
// Upon completion of the simple test step the next test step will be called.
// In case of polling test step the next test step is passed to the polling test step, and
// it is responsibility of the polling test step to call the next test step.
try {
if (step[TestClass.isPollingStepFlag]) {
step.call(this, nextTestStepTrigger);
} else {
step.call(this);
nextTestStepTrigger.call(this);
}
} catch (e) {
this._testCompleted();
Assert.ok(false, e.toString());
// done is QUnit callback indicating the end of the test
done();
return;
}
} else {
this._testCompleted();
// done is QUnit callback indicating the end of the test
done();
}
};
trigger();
} catch (ex) {
Assert.ok(false, "Unexpected Exception: " + ex);
this._testCompleted(true);
// done is QUnit callback indicating the end of the test
done();
}
};
// Register the test with QUnit
QUnit.test(testInfo.name, testMethod);
}
/** Register a Javascript unit testcase. */
public testCase(testInfo: TestCase) {
if (!testInfo.name) {
throw new Error("Must specify name in testInfo context in registerTestcase call");
}
if (!testInfo.test) {
throw new Error("Must specify 'test' method in testInfo context in registerTestcase call");
}
// Create a wrapper around the test method so we can do test initilization and cleanup.
var testMethod = () => {
// Save off the instance of the currently running suite.
TestClass.currentTestClass = this;
// Run the test.
try {
this._testStarting();
testInfo.test.call(this);
this._testCompleted();
}
catch (ex) {
Assert.ok(false, "Unexpected Exception: " + ex);
this._testCompleted(true);
}
};
// Register the test with QUnit
test(testInfo.name, testMethod);
}
/** Called when the test is starting. */
private _testStarting() {
// Initialize the sandbox similar to what is done in sinon.js "test()" override. See note on class.
var config = (<any>sinon).getConfig(sinon.config);
config.useFakeTimers = this.useFakeTimers;
config.useFakeServer = this.useFakeServer;
config.injectInto = config.injectIntoThis && this || config.injectInto;
this.sandbox = sinon.sandbox.create(config);
this.server = this.sandbox.server;
// Allow the derived class to perform test initialization.
this.testInitialize();
}
/** Called when the test is completed. */
private _testCompleted(failed?: boolean) {
if (failed) {
// Just cleanup the sandbox since the test has already failed.
this.sandbox.restore();
}
else {
// Verify the sandbox and restore.
(<any>this.sandbox).verifyAndRestore();
}
this.testCleanup();
// Clear the instance of the currently running suite.
TestClass.currentTestClass = null;
}
/**** Sinon methods and properties ***/
// These methods and properties are injected by Sinon and will override the implementation here.
// These are here purely to make typescript happy.
public clock: SinonFakeTimers;
public server: SinonFakeServer;
public sandbox: SinonSandbox;
/** Creates an anonymous function that records arguments, this value, exceptions and return values for all calls. */
public spy(): SinonSpy;
/** Spies on the provided function */
public spy(funcToWrap: Function): SinonSpy;
/** Creates a spy for object.methodName and replaces the original method with the spy. The spy acts exactly like the original method in all cases. The original method can be restored by calling object.methodName.restore(). The returned spy is the function object which replaced the original method. spy === object.method. */
public spy(object: any, methodName: string, func?: Function): SinonSpy;
public spy(...args: any[]): SinonSpy { return null; }
/** Creates an anonymous stub function. */
public stub(): SinonStub;
/** Stubs all the object's methods. */
public stub(object: any): SinonStub;
/** Replaces object.methodName with a func, wrapped in a spy. As usual, object.methodName.restore(); can be used to restore the original method. */
public stub(object: any, methodName: string, func?: Function): SinonStub;
public stub(...args: any[]): SinonStub { return null; }
/** Creates a mock for the provided object.Does not change the object, but returns a mock object to set expectations on the object's methods. */
public mock(object: any): SinonMock { return null; }
/**** end: Sinon methods and properties ***/
/** Sends a JSON response to the provided request.
* @param request The request to respond to.
* @param data Data to respond with.
* @param errorCode Optional error code to send with the request, default is 200
*/
public sendJsonResponse(request: SinonFakeXMLHttpRequest, data: any, errorCode?: number) {
if (errorCode === undefined) {
errorCode = 200;
}
request.respond(
errorCode,
{ "Content-Type": "application/json" },
JSON.stringify(data));
}
protected setUserAgent(userAgent: string) {
Object.defineProperty(window.navigator, 'userAgent',
{
configurable: true,
get: function () {
return userAgent;
}
});
}
}
// Configure Sinon
sinon.assert.fail = function (msg?) {
Assert.ok(false, msg);
};
sinon.assert.pass = function (assertion) {
Assert.ok(assertion, "sinon assert");
};
sinon.config = {
injectIntoThis: true,
injectInto: null,
properties: ["spy", "stub", "mock", "clock", "sandbox"],
useFakeTimers: true,
useFakeServer: true
};

Просмотреть файл

@ -0,0 +1,495 @@
/// <reference path='./TestFramework/Common.ts' />
import { ApplicationInsights, IApplicationInsights } from '../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
import { IDependencyTelemetry, ContextTagKeys, Util } from '@microsoft/applicationinsights-common';
export class ApplicationInsightsTests extends TestClass {
private static readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
private static readonly _expectedTrackMethods = [
"startTrackPage",
"stopTrackPage",
"trackException",
"trackEvent",
"trackMetric",
"trackPageView",
"trackTrace",
"trackDependencyData",
"setAuthenticatedUserContext",
"clearAuthenticatedUserContext",
"trackPageViewPerformance",
"addTelemetryInitializer",
"flush"
];
private _ai: IApplicationInsights;
private _aiName: string = 'AppInsightsSDK';
// Sinon
private errorSpy: SinonSpy;
private successSpy: SinonSpy;
private loggingSpy: SinonSpy;
private userSpy: SinonSpy;
// Context
private tagKeys = new ContextTagKeys();
public testInitialize() {
try {
this.useFakeServer = false;
(<any>sinon.fakeServer).restore();
this.useFakeTimers = false;
this.clock.restore();
var init = new ApplicationInsights({
config: {
instrumentationKey: ApplicationInsightsTests._instrumentationKey,
disableAjaxTracking: false,
disableFetchTracking: false,
extensionConfig: {
AppInsightsChannelPlugin: {
maxBatchInterval: 5000
},
ApplicationInsightsAnalytics: {
disableExceptionTracking: false
}
}
}
});
this._ai = init.loadAppInsights();
// Setup Sinon stuff
const sender: Sender = this._ai.appInsights.core['_channelController'].channelQueue[0][0];
this.errorSpy = this.sandbox.spy(sender, '_onError');
this.successSpy = this.sandbox.spy(sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(this._ai['core'].logger, 'throwInternal');
} catch (e) {
console.error('Failed to initialize');
}
}
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
}
public registerTests() {
this.addGenericE2ETests();
this.addAnalyticsApiTests();
this.addAsyncTests();
this.addDependencyPluginTests();
this.addPropertiesPluginTests();
}
public addGenericE2ETests(): void {
this.testCase({
name: 'E2E.GenericTests: ApplicationInsightsAnalytics is loaded correctly',
test: () => {
Assert.ok(this._ai, 'ApplicationInsights SDK exists');
// TODO: reenable this test when module is available from window w/o snippet
// Assert.deepEqual(this._ai, window[this._aiName], `AI is available from window.${this._aiName}`);
Assert.ok(this._ai.appInsights, 'App Analytics exists');
Assert.equal(true, this._ai.appInsights['_isInitialized'], 'App Analytics is initialized');
Assert.ok(this._ai.appInsights.core, 'Core exists');
Assert.equal(true, this._ai.appInsights.core['_isInitialized'],
'Core is initialized');
}
});
}
public addAnalyticsApiTests(): void {
this.testCase({
name: 'E2E.AnalyticsApiTests: Public Members exist',
test: () => {
ApplicationInsightsTests._expectedTrackMethods.forEach(method => {
Assert.ok(this._ai[method], `${method} exists`);
Assert.equal('function', typeof this._ai[method], `${method} is a function`);
});
}
});
}
public addAsyncTests(): void {
this.testCaseAsync({
name: 'E2E.GenericTests: trackEvent sends to backend',
stepDelay: 1,
steps: [() => {
this._ai.trackEvent({name: 'event', properties: {"prop1": "value1"}, measurements: {"measurement1": 200}});
}].concat(this.asserts(1)).concat(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
const payload = JSON.parse(payloadStr[0]);
let data = payload.data;
Assert.ok(data && data.baseData && data.baseData.properties["prop1"]);
Assert.ok(data && data.baseData && data.baseData.measurements["measurement1"]);
}
})
});
this.testCaseAsync({
name: 'E2E.GenericTests: trackTrace sends to backend',
stepDelay: 1,
steps: [() => {
this._ai.trackTrace({message: 'trace'});
}].concat(this.asserts(1))
});
this.testCaseAsync({
name: 'E2E.GenericTests: trackException sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
try {
window['a']['b']();
Assert.ok(false, 'trackException test not run');
} catch (e) {
exception = e;
this._ai.trackException({error: exception});
}
Assert.ok(exception);
}].concat(this.asserts(1))
});
this.testCaseAsync({
name: "TelemetryContext: track metric",
stepDelay: 1,
steps: [
() => {
console.log("* calling trackMetric " + new Date().toISOString());
for (var i = 0; i < 100; i++) {
this._ai.trackMetric({name: "test" + i, average: Math.round(100 * Math.random())});
}
console.log("* done calling trackMetric " + new Date().toISOString());
}
].concat(this.asserts(100))
});
this.testCaseAsync({
name: "TelemetryContext: track page view",
stepDelay: 1,
steps: [
() => {
this._ai.trackPageView({}); // sends 2
}
].concat(this.asserts(2))
});
this.testCaseAsync({
name: "TelemetryContext: track page view performance",
stepDelay: 1,
steps: [
() => {
this._ai.trackPageViewPerformance({name: 'name', url: 'url'});
}
].concat(this.asserts(1))
});
this.testCaseAsync({
name: "TelemetryContext: track all types in batch",
stepDelay: 1,
steps: [
() => {
var exception = null;
try {
window["a"]["b"]();
} catch (e) {
exception = e;
}
Assert.ok(exception);
this._ai.trackException({error: exception});
this._ai.trackMetric({name: "test", average: Math.round(100 * Math.random())});
this._ai.trackTrace({message: "test"});
this._ai.trackPageView({}); // sends 2
this._ai.trackPageViewPerformance({name: 'name', url:'http://someurl'});
}
].concat(this.asserts(6))
});
this.testCaseAsync({
name: "TelemetryContext: track all types in a large batch",
stepDelay: 1,
steps: [
() => {
var exception = null;
try {
window["a"]["b"]();
} catch (e) {
exception = e;
}
Assert.ok(exception);
for (var i = 0; i < 100; i++) {
this._ai.trackException({error: exception});
this._ai.trackMetric({name: "test", average: Math.round(100 * Math.random())});
this._ai.trackTrace({message: "test"});
this._ai.trackPageView({name: `${i}`}); // sends 2 1st time
}
}
].concat(this.asserts(401))
});
this.testCaseAsync({
name: "TelemetryInitializer: E2E override envelope data",
stepDelay: 1,
steps: [
() => {
// Setup
var telemetryInitializer = {
init: (envelope) => {
envelope.baseData.name = 'other name'
return true;
}
}
// Act
this._ai.addTelemetryInitializer(telemetryInitializer.init);
this._ai.trackMetric({name: "test", average: Math.round(100 * Math.random())});
}
]
.concat(this.asserts(1))
.concat(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
Assert.equal(1, payloadStr.length, 'Only 1 track item is sent');
const payload = JSON.parse(payloadStr[0]);
Assert.ok(payload);
if (payload && payload.baseData) {
const nameResult: string = payload.data.baseData.metrics[0].name;
const nameExpect: string = 'other name';
Assert.equal(nameExpect, nameResult, 'telemetryinitializer override successful');
}
}
})
});
}
public addDependencyPluginTests(): void {
this.testCaseAsync({
name: "TelemetryContext: trackDependencyData",
stepDelay: 1,
steps: [
() => {
const data: IDependencyTelemetry = {
target: 'http://abc',
responseCode: 200,
type: 'GET',
id: 'abc'
}
this._ai.trackDependencyData(data);
}
].concat(this.asserts(1))
});
this.testCaseAsync({
name: "TelemetryContext: auto collection of ajax requests",
stepDelay: 1,
steps: [
() => {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://httpbin.org/status/200');
xhr.send();
Assert.ok(true);
}
].concat(this.asserts(1))
});
if (window && window.fetch) {
this.testCaseAsync({
name: "DependenciesPlugin: auto collection of outgoing fetch requests",
stepDelay: 1,
steps: [
() => {
fetch('https://httpbin.org/status/200', { method: 'GET' });
Assert.ok(true, "fetch monitoring is instrumented");
}
].concat(this.asserts(1))
});
} else {
this.testCase({
name: "DependenciesPlugin: No crash when fetch not supported",
test: () => {
Assert.ok(true, "fetch monitoring is correctly not instrumented")
}
});
}
}
public addPropertiesPluginTests(): void {
this.testCaseAsync({
name: 'AuthenticatedUserContext: setAuthenticatedUserContext authId',
stepDelay: 1,
steps: [
() => {
this._ai.setAuthenticatedUserContext('10001');
this._ai.trackTrace({message: 'authUserContext test'});
}
]
.concat(this.asserts(1))
.concat(<any>PollingAssert.createPollingAssert(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
if (payloadStr.length !== 1) {
// Only 1 track should be sent
return false;
}
const payload = JSON.parse(payloadStr[0]);
if (payload && payload.tags) {
const tagName: string = this.tagKeys.userAuthUserId;
return '10001' === payload.tags[tagName];
}
}
return false;
}, 'user.authenticatedId', 5, 500))
});
this.testCaseAsync({
name: 'AuthenticatedUserContext: setAuthenticatedUserContext authId and accountId',
stepDelay: 1,
steps: [
() => {
this._ai.setAuthenticatedUserContext('10001', 'account123');
this._ai.trackTrace({message: 'authUserContext test'});
}
]
.concat(this.asserts(1))
.concat(<any>PollingAssert.createPollingAssert(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
if (payloadStr.length !== 1) {
// Only 1 track should be sent
return false;
}
const payload = JSON.parse(payloadStr[0]);
if (payload && payload.tags) {
const authTag: string = this.tagKeys.userAuthUserId;
const accountTag: string = this.tagKeys.userAccountId;
return '10001' === payload.tags[authTag] /*&&
'account123' === payload.tags[accountTag] */; //bug https://msazure.visualstudio.com/One/_workitems/edit/3508825
}
}
return false;
}, 'user.authenticatedId', 5, 500))
});
this.testCaseAsync({
name: 'AuthenticatedUserContext: setAuthenticatedUserContext non-ascii authId and accountId',
stepDelay: 1,
steps: [
() => {
this._ai.setAuthenticatedUserContext("\u0428", "\u0429");
this._ai.trackTrace({message: 'authUserContext test'});
}
]
.concat(this.asserts(1))
.concat(<any>PollingAssert.createPollingAssert(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
if (payloadStr.length !== 1) {
// Only 1 track should be sent
return false;
}
const payload = JSON.parse(payloadStr[0]);
if (payload && payload.tags) {
const authTag: string = this.tagKeys.userAuthUserId;
const accountTag: string = this.tagKeys.userAccountId;
return '\u0428' === payload.tags[authTag] /* &&
'\u0429' === payload.tags[accountTag] */; //bug https://msazure.visualstudio.com/One/_workitems/edit/3508825
}
}
return false;
}, 'user.authenticatedId', 5, 500))
});
this.testCaseAsync({
name: 'AuthenticatedUserContext: clearAuthenticatedUserContext',
stepDelay: 1,
steps: [
() => {
this._ai.setAuthenticatedUserContext('10002', 'account567');
this._ai.clearAuthenticatedUserContext();
this._ai.trackTrace({message: 'authUserContext test'});
}
]
.concat(this.asserts(1))
.concat(<any>PollingAssert.createPollingAssert(() => {
if (this.successSpy.called) {
const payloadStr: string[] = this.successSpy.args[0][0];
if (payloadStr.length !== 1) {
// Only 1 track should be sent
return false;
}
const payload = JSON.parse(payloadStr[0]);
if (payload && payload.tags) {
const authTag: string = this.tagKeys.userAuthUserId;
const accountTag: string = this.tagKeys.userAccountId;
return undefined === payload.tags[authTag] &&
undefined === payload.tags[accountTag];
}
}
return false;
}, 'user.authenticatedId', 5, 500))
});
// This doesn't need to be e2e
this.testCase({
name: 'AuthenticatedUserContext: setAuthenticatedUserContext does not set the cookie by default',
test: () => {
// Setup
const authSpy: SinonSpy = this.sandbox.spy(this._ai, 'setAuthenticatedUserContext');
const cookieSpy: SinonSpy = this.sandbox.spy(Util, 'setCookie');
// Act
this._ai.setAuthenticatedUserContext('10002', 'account567');
// Test
Assert.ok(authSpy.calledOnce, 'setAuthenticatedUserContext called');
Assert.equal(false, authSpy.calledWithExactly('10001', 'account567', false), 'Correct default args to setAuthenticatedUserContext');
Assert.ok(cookieSpy.notCalled, 'cookie never set');
}
});
}
private boilerPlateAsserts = () => {
Assert.ok(this.successSpy.called, "success");
Assert.ok(!this.errorSpy.called, "no error sending");
var isValidCallCount = this.loggingSpy.callCount === 0;
Assert.ok(isValidCallCount, "logging spy was called 0 time(s)");
if (!isValidCallCount) {
while (this.loggingSpy.args.length) {
Assert.ok(false, "[warning thrown]: " + this.loggingSpy.args.pop());
}
}
}
private asserts: any = (expectedCount: number) => [() => {
var message = "polling: " + new Date().toISOString();
Assert.ok(true, message);
console.log(message);
if (this.successSpy.called) {
this.boilerPlateAsserts();
this.testCleanup();
} else if (this.errorSpy.called || this.loggingSpy.called) {
this.boilerPlateAsserts();
}
},
(PollingAssert.createPollingAssert(() => {
Assert.ok(true, "* checking success spy " + new Date().toISOString());
if(this.successSpy.called) {
let currentCount: number = 0;
this.successSpy.args.forEach(call => {
currentCount += call[1];
});
console.log('curr: ' + currentCount + ' exp: ' + expectedCount);
return currentCount === expectedCount;
} else {
return false;
}
}, "sender succeeded", 30, 1000))];
}

Просмотреть файл

@ -0,0 +1 @@
require('nightwatch/bin/runner.js');

Просмотреть файл

@ -0,0 +1,137 @@
{
"src_folders" : ["Tests/nightwatch"],
"webdriver" : {
"start_process": true,
"port": 4444
},
"test_settings" : {
"chrome": {
"output_folder": "tests_output/chrome",
"webdriver": {
"port": 9515,
"server_path": "node_modules/chromedriver/lib/chromedriver/chromedriver",
"cli_args": [
"--verbose"
]
},
"desiredCapabilities": {
"browserName": "chrome",
"chromeOptions": {
"args": [
"--no-sandbox"
]
}
}
},
"firefox": {
"output_folder": "tests_output/firefox",
"webdriver": {
"start_process": true,
"port": 9516,
"server_path": "node_modules/geckodriver/geckodriver",
"cli_args": [
"--log",
"debug"
]
},
"desiredCapabilities": {
"browserName": "firefox",
"acceptInsecureCerts": true
}
},
"edge": {
"output_folder": "tests_output/edge",
"selenium_host": "127.0.0.1",
"selenium_port": 9517,
"selenium": {
"start_process": true,
"server_path": "node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-3.141.5.jar",
"log_path": "",
"cli_args": {
"webdriver.edge.driver": "C:/Windows/System32/MicrosoftWebDriver.exe"
}
},
"desiredCapabilities": {
"browserName": "MicrosoftEdge",
"acceptSslCerts": true
}
},
"ie11": {
"output_folder": "tests_output/ie11",
"selenium_host": "127.0.0.1",
"selenium_port": 9518,
"selenium": {
"start_process": true,
"silent": true,
"server_path": "node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-3.141.5.jar",
"cli_args": {
"webdriver.ie.driver": "./bin/IEDriverServer.exe"
}
},
"desiredCapabilities": {
"browserName": "internet explorer",
"version": 11,
"javascriptEnabled": true,
"acceptSslCerts": true
}
},
"ie10": {
"output_folder": "tests_output/ie10",
"selenium_host": "127.0.0.1",
"selenium_port": 9519,
"selenium": {
"start_process": true,
"silent": true,
"server_path": "node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-3.141.5.jar",
"cli_args": {
"webdriver.ie.driver": "./bin/IEDriverServer.exe"
}
},
"desiredCapabilities": {
"browserName": "internet explorer",
"version": 10,
"javascriptEnabled": true,
"acceptSslCerts": true
}
},
"ie9": {
"output_folder": "tests_output/ie9",
"selenium_host": "127.0.0.1",
"selenium_port": 9520,
"selenium": {
"start_process": true,
"silent": true,
"server_path": "node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-3.141.5.jar",
"cli_args": {
"webdriver.ie.driver": "./bin/IEDriverServer.exe"
}
},
"desiredCapabilities": {
"browserName": "internet explorer",
"version": 9,
"javascriptEnabled": true,
"acceptSslCerts": true
}
},
"ie8": {
"output_folder": "tests_output/ie8",
"selenium_host": "127.0.0.1",
"selenium_port": 9521,
"selenium": {
"start_process": true,
"silent": true,
"server_path": "node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-3.141.5.jar",
"cli_args": {
"webdriver.ie.driver": "./bin/IEDriverServer.exe"
}
},
"desiredCapabilities": {
"browserName": "internet explorer",
"version": 8,
"javascriptEnabled": true,
"acceptSslCerts": true
}
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
module.exports = {
'1. Navigate to tests' : function (browser) {
var reportName = `tests_output/qunit_output_${browser.options.desiredCapabilities.browserName}${browser.options.desiredCapabilities.version || ''}.html`;
var ieAppendage = browser.options.desiredCapabilities.version;
ieAppendage = ieAppendage ? `?version=${ieAppendage}` : '';
browser
.url(`http://localhost:8000/Tests/Selenium/Tests.html${ieAppendage}`)
.pause(1500);
var pollResults = () => {
try {
browser.elements('css selector', 'li[class*="fail"] [class*="test-name"]', (result) => {
if (result.value && result.value.length > 0) {
browser.getTitle(title => {
if (title.includes('✖')) {
saveHtml(browser, reportName);
clearTimeout(a);
browser.assert.ok(false, 'Tests failed');
browser.end();
} else {
a = setTimeout(pollResults, 1000);
browser.pause(1500);
}
});
}
});
browser.elements('css selector', 'li[class*="running"] [class*="test-name"]', (result) => {
if (result.value && result.value.length === 0) {
browser.getTitle(title => {
if (title.includes('✔')) {
saveHtml(browser, reportName);
clearTimeout(a);
browser.assert.ok(true, 'Tests passed');
browser.end();
} else {
a = setTimeout(pollResults, 1000);
browser.pause(1500);
}
})
} else {
a = setTimeout(pollResults, 1000);
browser.pause(1500);
}
});
} catch (e) {
browser.assert.ok(false, 'Tests failed due to error');
browser.end();
}
};
var a = setTimeout(pollResults, 1000);
}
};
function saveHtml(browser, reportName) {
browser.source(result => {
require('fs').writeFile(reportName, result.value);
})
}

Просмотреть файл

@ -0,0 +1,47 @@
var http = require('http');
var fs = require('fs');
var crypto = require('crypto');
var finalhandler = require('finalhandler');
var serveStatic = require('serve-static');
var PORT = process.env.PORT || 8000;
var serve = serveStatic(__dirname + "/../../");
var server = http.createServer(function(req, res) {
var url = req.url;
// Kill the server
if (url === '/_done') {
console.log('Exiting server_nightwatch.js');
res.write('<h1>Exiting</h1>');
res.end();
process.nextTick(() => {process.exit()});
// Special case - Add Internet Explorer emulation meta tag if query arg is present
} else if (url.startsWith('/Tests/Selenium/Tests.html?')) {
fs.readFile('Tests/Selenium/Tests.html', 'utf8', (err, html) => {
if (err) {
throw err;
}
// Grab version from query params and insert emulation meta tag into html
var version = parseInt(url.substring('version='.length+url.indexOf('version=')));
var ieEmulationMetaTag = `<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE${version}" />`;
var insertAfter = html.indexOf('<head>') + '<head>'.length;
html = html.substr(0, insertAfter) + ieEmulationMetaTag + html.substr(insertAfter);
// Send new html
res.writeHeader(200, {'Content-Type': 'text/html'});
res.write(html);
res.end();
});
// Serve static files normally
} else {
var done = finalhandler(req, res);
serve(req, res, done);
}
});
console.log(`Now listening at https://localhost:${PORT}}`)
server.listen(PORT);

Просмотреть файл

@ -0,0 +1,195 @@
/// <reference path='./TestFramework/Common.ts' />
import { ApplicationInsights, IApplicationInsights } from '../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
export class SanitizerE2ETests extends TestClass {
private readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
private _ai: IApplicationInsights;
// Sinon
private errorSpy: SinonSpy;
private successSpy: SinonSpy;
private loggingSpy: SinonSpy;
private delay = 100;
public testInitialize() {
try{
this.useFakeServer = false;
(<any>sinon.fakeServer).restore();
this.useFakeTimers = false;
this.clock.restore();
var init = new ApplicationInsights({
config: {
instrumentationKey: this._instrumentationKey,
extensionConfig: {
'AppInsightsChannelPlugin': {
maxBatchInterval: 500
}
}
},
queue: [],
version: 2.0
});
this._ai = init.loadAppInsights();
// Setup Sinon stuff
const sender: Sender = this._ai.appInsights.core['_channelController'].channelQueue[0][0];
this.errorSpy = this.sandbox.spy(sender, '_onError');
this.successSpy = this.sandbox.spy(sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(this._ai.appInsights.core.logger, 'throwInternal');
} catch (e) {
console.error('Failed to initialize');
}
}
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
}
public registerTests() {
this.addAsyncTests();
}
private addAsyncTests(): void {
var boilerPlateAsserts = () => {
Assert.ok(this.successSpy.called, "success");
Assert.ok(!this.errorSpy.called, "no error sending");
}
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts sanitized names",
stepDelay: this.delay,
steps: [
() => {
var properties = {
"property1%^~`": "hello",
"property2*&#+": "world"
};
var measurements = {
"measurement@|": 300
};
this._ai.trackMetric({name: "test", average: 5});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts legal charater set names",
stepDelay: this.delay,
steps: [
() => {
var properties = {
"abcdefghijklmnopqrstuvwxyz": "hello",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ": "world"
};
var measurements = {
"(1234567890/ \_-.)": 300
};
this._ai.trackMetric({name: "test", average: 5});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts up to 150 charaters for names",
stepDelay: this.delay,
steps: [
() => {
var len = 150;
var name = new Array(len + 1).join('a');
this._ai.trackMetric({name: name, average: 5});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts up to 1024 charaters for values",
stepDelay: this.delay,
steps: [
() => {
var len = 1024;
var value = new Array(len + 1).join('a');
var properties = {
"testProp": value
};
this._ai.trackMetric({name: "test", average: 5});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts up to 2048 charaters for url",
stepDelay: this.delay,
steps: [
() => {
var len = 2048;
var url = "http://hello.com/";
url = url + new Array(len - url.length + 1).join('a');
this._ai.trackPageView({name: "test", uri: url});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
this.testCaseAsync({
name: "Sanitizer2ETests: Data platform accepts up to 32768 charaters for messages",
stepDelay: this.delay,
steps: [
() => {
var len = 32768;
var message = new Array(len + 1).join('a');
this._ai.trackTrace({message: message, severityLevel: 0});
},
].concat(<any>PollingAssert.createPollingAssert(() => {
Assert.ok(true, "waiting for response " + new Date().toISOString());
return (this.successSpy.called || this.errorSpy.called || this.loggingSpy.called);
}, "Wait for response", 5, 1000))
.concat(() => {
boilerPlateAsserts();
})
});
}
}

Просмотреть файл

@ -0,0 +1,186 @@
/// <reference path='./TestFramework/Common.ts' />
"use strict"
import { ApplicationInsights, IApplicationInsights } from '../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
export class SenderE2ETests extends TestClass {
private readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
private readonly _bufferName = 'AI_buffer';
private readonly _sentBufferName = 'AI_sentBuffer';
private _ai: IApplicationInsights;
private _sender: Sender;
// Sinon
private errorSpy: SinonSpy;
private successSpy: SinonSpy;
private loggingSpy: SinonSpy;
private clearSpy: SinonSpy;
private delay = 100;
public testInitialize() {
try{
this.useFakeServer = false;
(<any>sinon.fakeServer).restore();
this.useFakeTimers = false;
this.clock.restore();
var init = new ApplicationInsights({
config: {
instrumentationKey: this._instrumentationKey,
loggingLevelConsole: 999,
extensionConfig: {
'AppInsightsChannelPlugin': {
maxBatchInterval: 2000,
maxBatchSizeInBytes: 10*1024*1024 // 10 MB
}
}
},
queue: [],
version: 2.0
});
this._ai = init.loadAppInsights();
// Setup Sinon stuff
this._sender = this._ai.appInsights.core['_channelController'].channelQueue[0][0];
this._sender._buffer.clear();
this.errorSpy = this.sandbox.spy(this._sender, '_onError');
this.successSpy = this.sandbox.spy(this._sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(this._ai.appInsights.core.logger, 'throwInternal');
this.clearSpy = this.sandbox.spy(this._sender._buffer, 'clearSent');
} catch (e) {
console.error('Failed to initialize');
}
}
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
this.successSpy.restore();
}
public registerTests() {
this.addAsyncTests();
this.addTrackEndpointTests();
this.addRetryTests();
}
private addRetryTests() {
var handle;
this.testCaseAsync({
name: 'Offline: offline telemetry is retried',
stepDelay: this.delay,
steps: [
() => {
handle = setInterval(() => {this._ai.trackTrace({message: 'intermittent message'})}, 500);
Assert.ok(true, 'sent event');
}
]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(<any>PollingAssert.createPollingAssert(() => {
let currentCount: number = 0;
if (this.successSpy.called) {
this.successSpy.args.forEach(call => {
const acceptedItems = call[1];
currentCount += acceptedItems; // number of accepted items
});
console.log('currentCount', currentCount);
return currentCount >= 20;
}
return false;
}, 'All items are sent', 600, 1000))
.concat(() => {
clearInterval(handle);
Assert.ok(true, 'handle cleared');
})
});
}
private addAsyncTests(): void {
this.testCaseAsync({
name: 'SendBuffer: Session storage is cleared after a send',
stepDelay: this.delay,
steps: [
() => {
this._ai.trackTrace({message: 'test trace'});
}
]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(<any>PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionSentEmpty(), "SentBuffer Session storage is empty", 5, 1000))
.concat(<any>PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionEmpty(), "Buffer Session storage is empty", 5, 1000))
});
}
private addTrackEndpointTests(): void {
const SENT_ITEMS: number = 100;
const SENT_TYPES: number = 4;
this.testCaseAsync({
name: 'EndpointTests: telemetry sent to endpoint fills to maxBatchSize',
stepDelay: this.delay,
steps: [
() => {
for (var i = 0; i < SENT_ITEMS; i++) {
this._ai.trackException({error: new Error()});
this._ai.trackMetric({name: "test", average: Math.round(100 * Math.random())});
this._ai.trackTrace({message: "test"});
this._ai.trackTrace({message: "test2"});
}
}
]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(<any>PollingAssert.createPollingAssert(() => {
let currentCount: number = 0;
if (this.successSpy.called) {
this.successSpy.args.forEach(call => {
const acceptedItems = call[1];
currentCount += acceptedItems; // number of accepted items
});
return currentCount === SENT_ITEMS * SENT_TYPES;
}
return false;
}, `Backend accepts ${SENT_ITEMS} items`, 5, 1000))
.concat(<any>PollingAssert.createPollingAssert(() => {
return this.successSpy.calledOnce;
}, "Tracks are sent in ONE batch", 5, 1000))
});
}
private waitForResponse() {
return <any>PollingAssert.createPollingAssert(() => {
return (this.successSpy.called || this.errorSpy.called);
}, "Wait for response" + new Date().toISOString(), 5, 1000)
}
private boilerPlateAsserts() {
Assert.ok(this.successSpy.called, "success");
Assert.ok(!this.errorSpy.called, "no error sending");
Assert.ok(this.clearSpy.called, "clearSent called");
var isValidCallCount = this.loggingSpy.callCount === 0;
Assert.ok(isValidCallCount, "logging spy was called 0 time(s)");
if (!isValidCallCount) {
while (this.loggingSpy.args.length) {
Assert.ok(false, "[warning thrown]: " + this.loggingSpy.args.pop());
}
}
}
private isSessionEmpty(): boolean {
let buffer: string = (<any>this._sender)._buffer.getBuffer(this._bufferName);
return buffer.length === 0;
}
private isSessionSentEmpty(): boolean {
let buffer: string = (<any>this._sender)._buffer.getBuffer(this._sentBufferName);
return buffer.length === 0;
}
}

Просмотреть файл

@ -0,0 +1,13 @@
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true,
"noImplicitAny": false,
"module": "amd",
"moduleResolution": "Node",
"target": "es5",
"alwaysStrict": true,
"declaration": true
},
"files": []
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,139 @@
/// <reference path='./TestFramework/Common.ts' />
import { ApplicationInsights, IApplicationInsights } from '../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
export class ValidateE2ETests extends TestClass {
private readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
private _ai: IApplicationInsights;
// Sinon
private errorSpy: SinonSpy;
private successSpy: SinonSpy;
private loggingSpy: SinonSpy;
private delay = 100;
public testInitialize() {
try{
this.useFakeServer = false;
(<any>sinon.fakeServer).restore();
this.useFakeTimers = false;
this.clock.restore();
var init = new ApplicationInsights({
config: {
instrumentationKey: this._instrumentationKey,
extensionConfig: {
'AppInsightsChannelPlugin': {
maxBatchInterval: 500
}
}
},
queue: [],
version: 2.0
});
this._ai = init.loadAppInsights();
// Setup Sinon stuff
const sender: Sender = this._ai.appInsights.core['_channelController'].channelQueue[0][0];
this.errorSpy = this.sandbox.spy(sender, '_onError');
this.successSpy = this.sandbox.spy(sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(this._ai.appInsights.core.logger, 'throwInternal');
} catch (e) {
console.error('Failed to initialize');
}
}
public testCleanup() {
this.useFakeServer = true;
this.useFakeTimers = true;
}
public registerTests() {
this.addAsyncTests();
}
private addAsyncTests(): void {
this.testCaseAsync({
name: "Validate track event",
stepDelay: this.delay,
steps: [
() => {
this._ai.trackTrace({message: "test"});
this._ai.trackTrace({message: "test event"}, { p1: "value 1", p2: "value 2", m1: 123, m2: 456.7 });
}]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(() => {
var acceptedItems = this.successSpy.args[0][1];
Assert.equal(2, acceptedItems, "backend should accept two events");
})
});
this.testCaseAsync({
name: "Validate that track event takes all type of characters",
stepDelay: this.delay,
steps: [
() => {
var s1 = "شلاؤيثبلاهتنمةىخحضقسفعشلاؤيصثبل";
var s2 = "Ինչու՞ նրանք չեն խոսում Հայերեն";
var s3 = "ওরা কন বাংলা বলেত পাের না";
var s4 = "妣 啊 僜刓嘰塡奬〉媆孿 偁偄偙 偁A偄E偆I偊O偍U";
var s5 = "ßüµ€ÄäÖö€ ερτυθιοπαδφγηξκλζχψωβνΔΦΓΗΞΚΛΨΩΘ რატომ";
var s6 = "йцуукенгшщзхъфываполджэс";
var s7 = "\x0000\x0001\x0002\x0003\x0004\x0005\x0006\x0007\x0008\x009F";
// white spaces
this._ai.trackTrace({message: " abcd efg "}, { " abc " : "value 1", " " : "value 2" });
// international characters
this._ai.trackTrace({message: s1}, { p: s2 });
this._ai.trackTrace({message: s3}, { p: s4 });
this._ai.trackTrace({message: s5}, { p: s6, p2: s7 });
}]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(() => {
var acceptedItems = this.successSpy.args[0][1];
Assert.equal(4, acceptedItems, "backend should accept all four events");
})
});
this.testCaseAsync({
name: "Validate that special characters are handled correctly",
stepDelay: this.delay,
steps: [
() => {
var s1 = "[]{};,.)(*&^%$#@/\\";
this._ai.trackTrace({message: s1}, { p: s1 });
this._ai.trackTrace({message: "a"}, { "[]{};,.)(*&^%$#@/\\": "b" });
}]
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(() => {
var acceptedItems = this.successSpy.args[0][1];
Assert.equal(2, acceptedItems, "backend should accept the event");
})
});
}
private waitForResponse() {
return <any>PollingAssert.createPollingAssert(() => {
return (this.successSpy.called || this.errorSpy.called);
}, "Wait for response" + new Date().toISOString(), 5, 1000)
}
private boilerPlateAsserts() {
Assert.ok(this.successSpy.called, "success");
Assert.ok(!this.errorSpy.called, "no error sending");
var isValidCallCount = this.loggingSpy.callCount === 0;
Assert.ok(isValidCallCount, "logging spy was called 0 time(s)");
if (!isValidCallCount) {
while (this.loggingSpy.args.length) {
Assert.ok(false, "[warning thrown]: " + this.loggingSpy.args.pop());
}
}
}
}

27
vNext/AISKU/tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,27 @@
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true,
"noImplicitAny": false,
"module": "es6",
"target": "es5",
"moduleResolution": "Node",
"alwaysStrict": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"importHelpers": true,
"noEmitHelpers": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"declarationDir": "vNext/AISKU/dist-esm",
"outDir": "dist-esm",
"rootDir": "vNext/AKSKU/src"
},
"include": [
"./src/**"
],
"exclude": [
"node_modules"
]
}